<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8>>,
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }
}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setab", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.apply_cap(cap_for_attr(attr), &[]),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]),
                         ("sgr", &[Param::Number(0)]),
                         ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| self.ti.strings.get(cap).map(|c| (c, params)))
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(self.out.write_all(&cmd));
        Ok(())
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.apply_cap("cuu1", &[])
    }

    fn delete_line(&mut self) -> Result<()> {
        self.apply_cap("dl", &[])
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.apply_cap("cr", &[])
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }

    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> Result<()> {
        match self.ti.strings.get(cmd) {
            Some(cmd) => {
                match expand(&cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(self.out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<commit_msg>rustfmt<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8>>,
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }
}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setab", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.apply_cap(cap_for_attr(attr), &[]),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]), ("sgr", &[Param::Number(0)]), ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| {
                                self.ti.strings.get(cap).map(|c| (c, params))
                            })
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(self.out.write_all(&cmd));
        Ok(())
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.apply_cap("cuu1", &[])
    }

    fn delete_line(&mut self) -> Result<()> {
        self.apply_cap("dl", &[])
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.apply_cap("cr", &[])
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }

    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> Result<()> {
        match self.ti.strings.get(cmd) {
            Some(cmd) => {
                match expand(&cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(self.out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::{ChanReader, ChanWriter, util, stdio};
use std::io::{Listener, Acceptor, File, BufferedReader, BufferedStream};
use std::os;
use std::str;
use std::task;

use curl::http;

// A "bomb" so when the test task exists we know when to shut down
// the server and fail if the subtask failed.
pub struct Bomb {
    accept: TcpAcceptor,
    rx: Receiver<()>,
    iorx: ChanReader,
}

impl Drop for Bomb {
    fn drop(&mut self) {
        self.accept.close_accept().unwrap();
        match self.rx.recv_opt() {
            Err(..) if !task::failing() => {
                fail!("server subtask failed: {}", self.iorx.read_to_string()
                                                            .unwrap());
            }
            _ => {}
        }
    }
}

pub fn proxy() -> (String, Bomb) {
    let me = task::name().unwrap();
    let record = os::getenv("RECORD").is_some();

    let mut l = TcpListener::bind("127.0.0.1", 0).unwrap();
    let ret = format!("http://{}", l.socket_name().unwrap());
    let mut a = l.listen().unwrap();
    let (tx, rx) = channel();

    let data = Path::new(file!()).dir_path().join("http-data")
                                 .join(me.as_slice().replace("::", "_"));
    let file = if record {File::create(&data)} else {File::open(&data)};
    let a2 = a.clone();

    let (iotx, iorx) = channel();
    let (iotx, iorx) = (ChanWriter::new(iotx), ChanReader::new(iorx));

    spawn(proc() {
        stdio::set_stderr(box iotx);
        let mut file = Some(file);
        let mut data = None;
        for socket in a.incoming() {
            let socket = match socket { Ok(s) => s, Err(..) => break };

            if data.is_none() {
                data = Some(BufferedStream::new(file.take().unwrap().unwrap()));
            }

            if record {
                record_http(socket, data.as_mut().unwrap());
            } else {
                replay_http(socket, data.as_mut().unwrap());
            }
        }
        match data {
            Some(ref mut f) => assert!(f.read_line().is_err()),
            None => {}
        }
        tx.send(());
    });

    (ret, Bomb { accept: a2, rx: rx, iorx: iorx })
}

fn record_http(mut socket: TcpStream, data: &mut BufferedStream<File>) {
    let (tx, rx) = channel();
    let (tx, mut rx) = (ChanWriter::new(tx), ChanReader::new(rx));
    let http_response = send(util::TeeReader::new(&mut socket as &mut Reader, tx));
    let request = rx.read_to_end().unwrap();

    let (tx, rx) = channel();
    let (tx, mut rx) = (ChanWriter::new(tx), ChanReader::new(rx));
    let socket = box socket as Box<Writer + 'static>;
    let tx = box tx as Box<Writer + 'static>;
    respond(http_response, util::MultiWriter::new(vec![socket, tx]));
    let response = rx.read_to_end().unwrap();

    (write!(data, "===REQUEST {}\n{}\n===RESPONSE {}\n{}\n",
            request.len(),
            str::from_utf8(request.as_slice()).unwrap(),
            response.len(),
            str::from_utf8(response.as_slice()).unwrap())).unwrap();

    fn send<R: Reader>(rdr: R) -> http::Response {
        let mut socket = BufferedReader::new(rdr);
        let method;
        let url;
        let mut headers = HashMap::new();
        {
            let mut lines = socket.lines();
            let line = lines.next().unwrap().unwrap();
            let mut parts = line.as_slice().split(' ');
            method = parts.next().unwrap().to_string();
            url = parts.next().unwrap().replace("http://", "https://");

            for line in lines {
                let line = line.unwrap();
                if line.len() < 3 { break }
                let mut parts = line.as_slice().splitn(1, ':');
                headers.insert(parts.next().unwrap().to_string(),
                               parts.next().unwrap().slice_from(1).to_string());
            }
        }

        let mut handle = http::handle();
        let mut req = match method.as_slice() {
            "PUT" => handle.put(url, &mut socket),
            "POST" => handle.post(url, &mut socket),
            "DELETE" => handle.delete(url),
            "GET" => handle.get(url),
            _ => fail!("unknown method: {}", method),
        };
        for (k, v) in headers.iter() {
            let v = v.as_slice().trim();
            match k.as_slice() {
                "Content-Length" => req = req.content_length(from_str(v).unwrap()),
                "Content-Type" => req = req.content_type(v),
                "Transfer-Encoding" => {}
                k => req = req.header(k, v),
            }
        }
        req.exec().unwrap()
    }

    fn respond<W: Writer>(response: http::Response, mut socket: W) {
        socket.write_str(format!("HTTP/1.1 {}\r\n",
                                 response.get_code()).as_slice())
              .unwrap();
        for (k, v) in response.get_headers().iter() {
            if k.as_slice() == "transfer-encoding" { continue }
            for v in v.iter() {
                socket.write(k.as_bytes()).unwrap();
                socket.write(b": ").unwrap();
                socket.write(v.as_bytes()).unwrap();
                socket.write(b"\r\n").unwrap();
            }
        }
        socket.write(b"\r\n").unwrap();
        socket.write(response.get_body()).unwrap();
    }
}

fn replay_http(socket: TcpStream, data: &mut BufferedStream<File>) {
    let mut writer = socket.clone();
    let mut socket = BufferedReader::new(socket);

    let request = data.read_line().unwrap();
    let mut request = request.as_slice().split(' ');
    assert_eq!(request.next().unwrap().as_slice(), "===REQUEST");
    let request_size: uint = from_str(request.next().unwrap().trim()).unwrap();

    let expected = data.read_exact(request_size).unwrap();
    let mut expected_lines = expected.as_slice().split(|b| *b == b'\n')
                                     .map(|s| str::from_utf8(s).unwrap())
                                     .map(|s| format!("{}\n", s));
    let mut actual_lines = socket.lines().map(|s| s.unwrap());

    // validate the headers
    let mut expected: HashSet<String> = expected_lines.by_ref()
                                                      .take_while(|l| l.len() > 2)
                                                      .collect();
    for line in actual_lines.by_ref().take_while(|l| l.len() > 2) {
        if expected.remove(&line) { continue }
        if line.as_slice().starts_with("Date:") { continue }
        if line.as_slice().starts_with("Authorization:") { continue }
        fail!("unexpected header: {}", line);
    }
    for line in expected.iter() {
        if line.as_slice().starts_with("Date:") { continue }
        if line.as_slice().starts_with("Authorization:") { continue }
        fail!("didn't receive header: {}", line);
    }

    // TODO: validate the body

    data.read_line().unwrap();
    let response = data.read_line().unwrap();
    let mut response = response.as_slice().split(' ');
    assert_eq!(response.next().unwrap().as_slice(), "===RESPONSE");
    let response_size: uint = from_str(response.next().unwrap().trim()).unwrap();
    let response = data.read_exact(response_size).unwrap();
    let mut lines = response.as_slice().split(|b| *b == b'\n')
                            .map(|s| str::from_utf8(s).unwrap());
    for line in lines {
        if line.starts_with("Date:") { continue }
        writer.write_str(line).unwrap();
        writer.write(b"\r\n").unwrap();
    }
    data.read_line().unwrap();
}
<commit_msg>Try to diagnose travis<commit_after>use std::collections::{HashMap, HashSet};
use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::{ChanReader, ChanWriter, util, stdio};
use std::io::{Listener, Acceptor, File, BufferedReader, BufferedStream};
use std::os;
use std::str;
use std::task;

use curl::http;

// A "bomb" so when the test task exists we know when to shut down
// the server and fail if the subtask failed.
pub struct Bomb {
    accept: TcpAcceptor,
    rx: Receiver<()>,
    iorx: ChanReader,
}

impl Drop for Bomb {
    fn drop(&mut self) {
        self.accept.close_accept().unwrap();
        let res = self.rx.recv_opt();
        let stderr = self.iorx.read_to_string().unwrap();
        match res {
            Err(..) if !task::failing() => {
                fail!("server subtask failed: {}", stderr)
            }
            _ => {
                if stderr.len() > 0 {
                    println!("server subtask failed: {}", stderr)
                }
            }
        }
    }
}

pub fn proxy() -> (String, Bomb) {
    let me = task::name().unwrap();
    let record = os::getenv("RECORD").is_some();

    let mut l = TcpListener::bind("127.0.0.1", 0).unwrap();
    let ret = format!("http://{}", l.socket_name().unwrap());
    let mut a = l.listen().unwrap();
    let (tx, rx) = channel();

    let data = Path::new(file!()).dir_path().join("http-data")
                                 .join(me.as_slice().replace("::", "_"));
    let file = if record {File::create(&data)} else {File::open(&data)};
    let a2 = a.clone();

    let (iotx, iorx) = channel();
    let (iotx, iorx) = (ChanWriter::new(iotx), ChanReader::new(iorx));

    spawn(proc() {
        stdio::set_stderr(box iotx);
        let mut file = Some(file);
        let mut data = None;
        for socket in a.incoming() {
            let socket = match socket { Ok(s) => s, Err(..) => break };

            if data.is_none() {
                data = Some(BufferedStream::new(file.take().unwrap().unwrap()));
            }

            if record {
                record_http(socket, data.as_mut().unwrap());
            } else {
                replay_http(socket, data.as_mut().unwrap());
            }
        }
        match data {
            Some(ref mut f) => assert!(f.read_line().is_err()),
            None => {}
        }
        tx.send(());
    });

    (ret, Bomb { accept: a2, rx: rx, iorx: iorx })
}

fn record_http(mut socket: TcpStream, data: &mut BufferedStream<File>) {
    let (tx, rx) = channel();
    let (tx, mut rx) = (ChanWriter::new(tx), ChanReader::new(rx));
    let http_response = send(util::TeeReader::new(&mut socket as &mut Reader, tx));
    let request = rx.read_to_end().unwrap();

    let (tx, rx) = channel();
    let (tx, mut rx) = (ChanWriter::new(tx), ChanReader::new(rx));
    let socket = box socket as Box<Writer + 'static>;
    let tx = box tx as Box<Writer + 'static>;
    respond(http_response, util::MultiWriter::new(vec![socket, tx]));
    let response = rx.read_to_end().unwrap();

    (write!(data, "===REQUEST {}\n{}\n===RESPONSE {}\n{}\n",
            request.len(),
            str::from_utf8(request.as_slice()).unwrap(),
            response.len(),
            str::from_utf8(response.as_slice()).unwrap())).unwrap();

    fn send<R: Reader>(rdr: R) -> http::Response {
        let mut socket = BufferedReader::new(rdr);
        let method;
        let url;
        let mut headers = HashMap::new();
        {
            let mut lines = socket.lines();
            let line = lines.next().unwrap().unwrap();
            let mut parts = line.as_slice().split(' ');
            method = parts.next().unwrap().to_string();
            url = parts.next().unwrap().replace("http://", "https://");

            for line in lines {
                let line = line.unwrap();
                if line.len() < 3 { break }
                let mut parts = line.as_slice().splitn(1, ':');
                headers.insert(parts.next().unwrap().to_string(),
                               parts.next().unwrap().slice_from(1).to_string());
            }
        }

        let mut handle = http::handle();
        let mut req = match method.as_slice() {
            "PUT" => handle.put(url, &mut socket),
            "POST" => handle.post(url, &mut socket),
            "DELETE" => handle.delete(url),
            "GET" => handle.get(url),
            _ => fail!("unknown method: {}", method),
        };
        for (k, v) in headers.iter() {
            let v = v.as_slice().trim();
            match k.as_slice() {
                "Content-Length" => req = req.content_length(from_str(v).unwrap()),
                "Content-Type" => req = req.content_type(v),
                "Transfer-Encoding" => {}
                k => req = req.header(k, v),
            }
        }
        req.exec().unwrap()
    }

    fn respond<W: Writer>(response: http::Response, mut socket: W) {
        socket.write_str(format!("HTTP/1.1 {}\r\n",
                                 response.get_code()).as_slice())
              .unwrap();
        for (k, v) in response.get_headers().iter() {
            if k.as_slice() == "transfer-encoding" { continue }
            for v in v.iter() {
                socket.write(k.as_bytes()).unwrap();
                socket.write(b": ").unwrap();
                socket.write(v.as_bytes()).unwrap();
                socket.write(b"\r\n").unwrap();
            }
        }
        socket.write(b"\r\n").unwrap();
        socket.write(response.get_body()).unwrap();
    }
}

fn replay_http(socket: TcpStream, data: &mut BufferedStream<File>) {
    let mut writer = socket.clone();
    let mut socket = BufferedReader::new(socket);

    let request = data.read_line().unwrap();
    let mut request = request.as_slice().split(' ');
    assert_eq!(request.next().unwrap().as_slice(), "===REQUEST");
    let request_size: uint = from_str(request.next().unwrap().trim()).unwrap();

    let expected = data.read_exact(request_size).unwrap();
    let mut expected_lines = expected.as_slice().split(|b| *b == b'\n')
                                     .map(|s| str::from_utf8(s).unwrap())
                                     .map(|s| format!("{}\n", s));
    let mut actual_lines = socket.lines().map(|s| s.unwrap());

    // validate the headers
    let mut expected: HashSet<String> = expected_lines.by_ref()
                                                      .take_while(|l| l.len() > 2)
                                                      .collect();
    for line in actual_lines.by_ref().take_while(|l| l.len() > 2) {
        if expected.remove(&line) { continue }
        if line.as_slice().starts_with("Date:") { continue }
        if line.as_slice().starts_with("Authorization:") { continue }
        fail!("unexpected header: {}", line);
    }
    for line in expected.iter() {
        if line.as_slice().starts_with("Date:") { continue }
        if line.as_slice().starts_with("Authorization:") { continue }
        fail!("didn't receive header: {}", line);
    }

    // TODO: validate the body

    data.read_line().unwrap();
    let response = data.read_line().unwrap();
    let mut response = response.as_slice().split(' ');
    assert_eq!(response.next().unwrap().as_slice(), "===RESPONSE");
    let response_size: uint = from_str(response.next().unwrap().trim()).unwrap();
    let response = data.read_exact(response_size).unwrap();
    let mut lines = response.as_slice().split(|b| *b == b'\n')
                            .map(|s| str::from_utf8(s).unwrap());
    for line in lines {
        if line.starts_with("Date:") { continue }
        writer.write_str(line).unwrap();
        writer.write(b"\r\n").unwrap();
    }
    data.read_line().unwrap();
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate unicase;
extern crate iron;
extern crate router;

use self::iron::{AfterMiddleware, headers, status};
use self::iron::method::Method;
use self::iron::method::Method::*;
use self::iron::prelude::*;
use self::router::Router;
use self::unicase::UniCase;

type Endpoint = (Method, &'static[&'static str]);

struct CORS;

impl CORS {
    // Only endpoints listed here will allow CORS.
    // Endpoints containing a variable path part can use '*' like in:
    // &["users", "*"]
    pub const ENDPOINTS: &'static[Endpoint] = &[
        (Method::Post,      &["invitations"]),
        (Method::Get,       &["invitations"]),
        (Method::Delete,    &["invitations"]),
        (Method::Post,      &["users"]),
        (Method::Get,       &["users"]),
        (Method::Put,       &["users", "*"]),
        (Method::Post,      &["users", "*"]),
        (Method::Post,      &["recoveries", "*"]),
        (Method::Get,       &["recoveries", "*", "*"]),
        (Method::Get,       &["permissions"]),
        (Method::Get,       &["permissions", "*"]),
        (Method::Get,       &["permissions", "*", "*"]),
        (Method::Get,       &["permissions", "_", "*"]),
        (Method::Put,       &["permissions", "*", "*"]),
    ];
}

impl AfterMiddleware for CORS {
    fn after(&self, req: &mut Request, mut res: Response)
        -> IronResult<Response> {

        let mut is_cors_endpoint = false;
        for endpoint in CORS::ENDPOINTS {
            let (ref method, path) = *endpoint;
            if req.method != *method {
                continue;
            }
            if path.len() != req.url.path.len() {
                continue;
            }
            for (i, path) in path.iter().enumerate() {
                is_cors_endpoint = false;
                if req.url.path[i] != path.to_string() &&
                   "*".to_string() != path.to_string() {
                    break;
                }
                is_cors_endpoint = true;
            }
            if is_cors_endpoint {
                break;
            }
        }

        if !is_cors_endpoint {
            return Ok(res);
        }

        res.headers.set(headers::AccessControlAllowOrigin::Any);
        res.headers.set(headers::AccessControlAllowHeaders(
                vec![UniCase("accept".to_string()),
                UniCase("content-type".to_string())]));
        res.headers.set(headers::AccessControlAllowMethods(
                vec![Get,Head,Post,Delete,Options,Put,Patch]));
        Ok(res)
    }
}

pub struct UsersRouter;

impl UsersRouter {
    fn not_implemented(_: &mut Request) -> IronResult<Response> {
        println!("Not implemented");
        Ok(Response::with(status::NotImplemented))
    }

    pub fn new() -> iron::middleware::Chain {
        let mut router = Router::new();

        router.post("/setup", UsersRouter::not_implemented);

        router.post("/invitations", UsersRouter::not_implemented);
        router.get("/invitations", UsersRouter::not_implemented);
        router.delete("invitations", UsersRouter::not_implemented);

        router.post("/users", UsersRouter::not_implemented);
        router.get("/users", UsersRouter::not_implemented);
        router.put("/users/:id", UsersRouter::not_implemented);
        router.post("/users/:id", UsersRouter::not_implemented);

        router.post("/recoveries/:user", UsersRouter::not_implemented);
        router.get("/recoveries/:user/:id", UsersRouter::not_implemented);

        router.get("/permissions", UsersRouter::not_implemented);
        router.get("/permissions/:user", UsersRouter::not_implemented);
        router.get("/permissions/:user/:taxon", UsersRouter::not_implemented);
        router.get("/permissions/_/:taxon", UsersRouter::not_implemented);
        router.put("/permissions/:user/:taxon", UsersRouter::not_implemented);

        let mut chain = Chain::new(router);
        chain.link_after(CORS);

        chain
    }
}

#[test]
fn test_cors_allowed_endpoints() {
    for endpoint in CORS::ENDPOINTS {
        assert!(true);
    }
}

#[test]
fn test_cors_not_allowed() {
    assert!(true);
}
<commit_msg>CORS tests<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate unicase;
extern crate iron;
extern crate router;

use self::iron::{AfterMiddleware, headers, status};
use self::iron::method::Method;
use self::iron::method::Method::*;
use self::iron::prelude::*;
use self::router::Router;
use self::unicase::UniCase;

type Endpoint = (Method, &'static[&'static str]);

struct CORS;

impl CORS {
    // Only endpoints listed here will allow CORS.
    // Endpoints containing a variable path part can use '*' like in:
    // &["users", "*"]
    pub const ENDPOINTS: &'static[Endpoint] = &[
        (Method::Post,      &["invitations"]),
        (Method::Get,       &["invitations"]),
        (Method::Delete,    &["invitations"]),
        (Method::Post,      &["users"]),
        (Method::Get,       &["users"]),
        (Method::Put,       &["users", "*"]),
        (Method::Post,      &["users", "*"]),
        (Method::Post,      &["recoveries", "*"]),
        (Method::Get,       &["recoveries", "*", "*"]),
        (Method::Get,       &["permissions"]),
        (Method::Get,       &["permissions", "*"]),
        (Method::Get,       &["permissions", "*", "*"]),
        (Method::Get,       &["permissions", "_", "*"]),
        (Method::Put,       &["permissions", "*", "*"]),
    ];
}

impl AfterMiddleware for CORS {
    fn after(&self, req: &mut Request, mut res: Response)
        -> IronResult<Response> {

        let mut is_cors_endpoint = false;
        for endpoint in CORS::ENDPOINTS {
            let (ref method, path) = *endpoint;
            if req.method != *method {
                continue;
            }
            if path.len() != req.url.path.len() {
                continue;
            }
            for (i, path) in path.iter().enumerate() {
                is_cors_endpoint = false;
                if req.url.path[i] != path.to_string() &&
                   "*".to_string() != path.to_string() {
                    break;
                }
                is_cors_endpoint = true;
            }
            if is_cors_endpoint {
                break;
            }
        }

        if !is_cors_endpoint {
            return Ok(res);
        }

        res.headers.set(headers::AccessControlAllowOrigin::Any);
        res.headers.set(headers::AccessControlAllowHeaders(
                vec![UniCase("accept".to_string()),
                UniCase("content-type".to_string())]));
        res.headers.set(headers::AccessControlAllowMethods(
                vec![Get,Head,Post,Delete,Options,Put,Patch]));
        Ok(res)
    }
}

pub struct UsersRouter;

impl UsersRouter {
    fn not_implemented(_: &mut Request) -> IronResult<Response> {
        println!("Not implemented");
        Ok(Response::with(status::NotImplemented))
    }

    pub fn new() -> iron::middleware::Chain {
        let mut router = Router::new();

        router.post("/setup", UsersRouter::not_implemented);

        router.post("/invitations", UsersRouter::not_implemented);
        router.get("/invitations", UsersRouter::not_implemented);
        router.delete("invitations", UsersRouter::not_implemented);

        router.post("/users", UsersRouter::not_implemented);
        router.get("/users", UsersRouter::not_implemented);
        router.put("/users/:id", UsersRouter::not_implemented);
        router.post("/users/:id", UsersRouter::not_implemented);

        router.post("/recoveries/:user", UsersRouter::not_implemented);
        router.get("/recoveries/:user/:id", UsersRouter::not_implemented);

        router.get("/permissions", UsersRouter::not_implemented);
        router.get("/permissions/:user", UsersRouter::not_implemented);
        router.get("/permissions/:user/:taxon", UsersRouter::not_implemented);
        router.get("/permissions/_/:taxon", UsersRouter::not_implemented);
        router.put("/permissions/:user/:taxon", UsersRouter::not_implemented);

        let mut chain = Chain::new(router);
        chain.link_after(CORS);

        chain
    }
}

#[test]
fn test_cors_allowed_endpoints() {
    use std::net::ToSocketAddrs;
    use self::iron::{method, TypeMap, Url};

    // Stub request
    fn request<'a, 'b>(method: &Method, path: &str) -> Request<'a, 'b> {
        let path = "http://localhost:3000/".to_string() + path;
        Request {
            url: Url::parse(&path).unwrap(),
            remote_addr:
                "localhost:3000".to_socket_addrs().unwrap().next().unwrap(),
            local_addr:
                "localhost:3000".to_socket_addrs().unwrap().next().unwrap(),
            headers: headers::Headers::new(),
            body: unsafe { ::std::mem::uninitialized() },
            method: method.clone(),
            extensions: TypeMap::new()
        }
    }

    // Test that all CORS allowed endpoints get the appropriate CORS headers.
    for endpoint in CORS::ENDPOINTS {
        let (ref method, path) = *endpoint;
        let path = path.join("/");
        let mut req = request(method, &path);
        match CORS.after(&mut req, Response::new()) {
            Ok(res) => {
                let headers = &res.headers;
                assert!(headers.has::<headers::AccessControlAllowOrigin>());
                assert!(headers.has::<headers::AccessControlAllowHeaders>());
                assert!(headers.has::<headers::AccessControlAllowMethods>());
            },
            _ => assert!(false)
        }
    }

    // Test that non-CORS-allowed endpoints like POST /setup don't get CORS
    // headers in the response.
    let mut req = request(&method::Post, "/setup");
    match CORS.after(&mut req, Response::new()) {
        Ok(res) => {
            let headers = &res.headers;
            assert!(!headers.has::<headers::AccessControlAllowOrigin>());
            assert!(!headers.has::<headers::AccessControlAllowHeaders>());
            assert!(!headers.has::<headers::AccessControlAllowMethods>());
        },
        _ => assert!(false)
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (path) eacces

#[macro_use]
extern crate uucore;

use clap::{crate_version, Arg, Command};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::format_usage;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    Never,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
const USAGE: &str = "{} [OPTION]... FILE...";
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_R: &str = "recursive_R";
static OPT_VERBOSE: &str = "verbose";
static PRESUME_INPUT_TTY: &str = "-presume-input-tty";

static ARG_FILES: &str = "files";

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r or -R)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let long_usage = get_long_usage();

    let matches = uu_app().after_help(&long_usage[..]).get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.is_present(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        return Err(UUsageError::new(1, "missing operand"));
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.is_present(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.is_present(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.is_present(OPT_INTERACTIVE) {
                    match matches.value_of(OPT_INTERACTIVE).unwrap() {
                        "never" => InteractiveMode::Never,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => {
                            return Err(USimpleError::new(
                                1,
                                format!("Invalid argument to interactive ({})", val),
                            ))
                        }
                    }
                } else {
                    InteractiveMode::Never
                }
            },
            one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT),
            recursive: matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_R),
            dir: matches.is_present(OPT_DIR),
            verbose: matches.is_present(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return Ok(());
            }
        }

        if remove(&files, &options) {
            return Err(1.into());
        }
    }
    Ok(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .infer_long_args(true)
        .arg(
            Arg::new(OPT_FORCE)
            .short('f')
            .long(OPT_FORCE)
            .multiple_occurrences(true)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::new(OPT_PROMPT)
            .short('i')
            .long("prompt before every removal")
        )
        .arg(
            Arg::new(OPT_PROMPT_MORE)
            .short('I')
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::new(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::new(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::new(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::new(OPT_RECURSIVE).short('r')
            .multiple_occurrences(true)
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            // To mimic GNU's behavior we also want the '-R' flag. However, using clap's
            // alias method 'visible_alias("R")' would result in a long '--R' flag.
            Arg::new(OPT_RECURSIVE_R).short('R')
            .help("Equivalent to -r")
        )
        .arg(
            Arg::new(OPT_DIR)
            .short('d')
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::new(OPT_VERBOSE)
            .short('v')
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        // From the GNU source code:
        // This is solely for testing.
        // Do not document.
        // It is relatively difficult to ensure that there is a tty on stdin.
        // Since rm acts differently depending on that, without this option,
        // it'd be harder to test the parts of rm that depend on that setting.
        // In contrast with Arg::long, Arg::alias does not strip leading
        // hyphens. Therefore it supports 3 leading hyphens.
        .arg(
            Arg::new(PRESUME_INPUT_TTY)
            .long(PRESUME_INPUT_TTY)
            .alias(PRESUME_INPUT_TTY)
            .hide(true)
        )
        .arg(
            Arg::new(ARG_FILES)
            .multiple_occurrences(true)
            .takes_value(true)
            .min_values(1)
            .value_hint(clap::ValueHint::AnyPath)
        )
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: &[String], options: &Options) -> bool {
    let mut had_err = false;

    for filename in files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, options)
                } else {
                    remove_file(file, options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!(
                        "cannot remove {}: No such file or directory",
                        filename.quote()
                    );
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always && !options.verbose {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    // here, GNU doesn't use some kind of remove_dir_all
                    // It will show directory+file
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in {}: {}", path.quote(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory {}", path.quote());
        had_err = true;
    } else {
        show_error!(
            "cannot remove {}: Is a directory", // GNU's rm error message does not include help
            path.quote()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        if let Ok(mut read_dir) = fs::read_dir(path) {
            if options.dir || options.recursive {
                if read_dir.next().is_none() {
                    match fs::remove_dir(path) {
                        Ok(_) => {
                            if options.verbose {
                                println!("removed directory {}", normalize(path).quote());
                            }
                        }
                        Err(e) => {
                            if e.kind() == std::io::ErrorKind::PermissionDenied {
                                // GNU compatibility (rm/fail-eacces.sh)
                                show_error!(
                                    "cannot remove {}: {}",
                                    path.quote(),
                                    "Permission denied"
                                );
                            } else {
                                show_error!("cannot remove {}: {}", path.quote(), e);
                            }
                            return true;
                        }
                    }
                } else {
                    // directory can be read but is not empty
                    show_error!("cannot remove {}: Directory not empty", path.quote());
                    return true;
                }
            } else {
                // called to remove a symlink_dir (windows) without "-r"/"-R" or "-d"
                show_error!("cannot remove {}: Is a directory", path.quote());
                return true;
            }
        } else {
            // GNU's rm shows this message if directory is empty but not readable
            show_error!("cannot remove {}: Directory not empty", path.quote());
            return true;
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed {}", normalize(path).quote());
                }
            }
            Err(e) => {
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory {}? ", path.quote())))
    } else {
        prompt(&(format!("rm: remove file {}? ", path.quote())))
    }
}

fn normalize(path: &Path) -> PathBuf {
    // copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
    // both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
    // for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
    // TODO: replace this once that lands
    uucore::fs::normalize_path(path)
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    match stdin.read_until(b'\n', &mut buf) {
        Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'),
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<commit_msg>rm: fix help text mistakenly being used as the long option<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (path) eacces

#[macro_use]
extern crate uucore;

use clap::{crate_version, Arg, Command};
use remove_dir_all::remove_dir_all;
use std::collections::VecDeque;
use std::fs;
use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr;
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::format_usage;
use walkdir::{DirEntry, WalkDir};

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    Never,
    Once,
    Always,
}

struct Options {
    force: bool,
    interactive: InteractiveMode,
    #[allow(dead_code)]
    one_fs: bool,
    preserve_root: bool,
    recursive: bool,
    dir: bool,
    verbose: bool,
}

static ABOUT: &str = "Remove (unlink) the FILE(s)";
const USAGE: &str = "{} [OPTION]... FILE...";
static OPT_DIR: &str = "dir";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_FORCE: &str = "force";
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PRESERVE_ROOT: &str = "preserve-root";
static OPT_PROMPT: &str = "prompt";
static OPT_PROMPT_MORE: &str = "prompt-more";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_R: &str = "recursive_R";
static OPT_VERBOSE: &str = "verbose";
static PRESUME_INPUT_TTY: &str = "-presume-input-tty";

static ARG_FILES: &str = "files";

fn get_long_usage() -> String {
    String::from(
        "By default, rm does not remove directories.  Use the --recursive (-r or -R)
        option to remove each listed directory, too, along with all of its contents

        To remove a file whose name starts with a '-', for example '-foo',
        use one of these commands:
        rm -- -foo

        rm ./-foo

        Note that if you use rm to remove a file, it might be possible to recover
        some of its contents, given sufficient expertise and/or time.  For greater
        assurance that the contents are truly unrecoverable, consider using shred.",
    )
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let long_usage = get_long_usage();

    let matches = uu_app().after_help(&long_usage[..]).get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let force = matches.is_present(OPT_FORCE);

    if files.is_empty() && !force {
        // Still check by hand and not use clap
        // Because "rm -f" is a thing
        return Err(UUsageError::new(1, "missing operand"));
    } else {
        let options = Options {
            force,
            interactive: {
                if matches.is_present(OPT_PROMPT) {
                    InteractiveMode::Always
                } else if matches.is_present(OPT_PROMPT_MORE) {
                    InteractiveMode::Once
                } else if matches.is_present(OPT_INTERACTIVE) {
                    match matches.value_of(OPT_INTERACTIVE).unwrap() {
                        "never" => InteractiveMode::Never,
                        "once" => InteractiveMode::Once,
                        "always" => InteractiveMode::Always,
                        val => {
                            return Err(USimpleError::new(
                                1,
                                format!("Invalid argument to interactive ({})", val),
                            ))
                        }
                    }
                } else {
                    InteractiveMode::Never
                }
            },
            one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM),
            preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT),
            recursive: matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_R),
            dir: matches.is_present(OPT_DIR),
            verbose: matches.is_present(OPT_VERBOSE),
        };
        if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) {
            let msg = if options.recursive {
                "Remove all arguments recursively? "
            } else {
                "Remove all arguments? "
            };
            if !prompt(msg) {
                return Ok(());
            }
        }

        if remove(&files, &options) {
            return Err(1.into());
        }
    }
    Ok(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(format_usage(USAGE))
        .infer_long_args(true)
        .arg(
            Arg::new(OPT_FORCE)
            .short('f')
            .long(OPT_FORCE)
            .multiple_occurrences(true)
            .help("ignore nonexistent files and arguments, never prompt")
        )
        .arg(
            Arg::new(OPT_PROMPT)
            .short('i')
            .help("prompt before every removal")
        )
        .arg(
            Arg::new(OPT_PROMPT_MORE)
            .short('I')
            .help("prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes")
        )
        .arg(
            Arg::new(OPT_INTERACTIVE)
            .long(OPT_INTERACTIVE)
            .help("prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always")
            .value_name("WHEN")
            .takes_value(true)
        )
        .arg(
            Arg::new(OPT_ONE_FILE_SYSTEM)
            .long(OPT_ONE_FILE_SYSTEM)
            .help("when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::new(OPT_NO_PRESERVE_ROOT)
            .long(OPT_NO_PRESERVE_ROOT)
            .help("do not treat '/' specially")
        )
        .arg(
            Arg::new(OPT_PRESERVE_ROOT)
            .long(OPT_PRESERVE_ROOT)
            .help("do not remove '/' (default)")
        )
        .arg(
            Arg::new(OPT_RECURSIVE).short('r')
            .multiple_occurrences(true)
            .long(OPT_RECURSIVE)
            .help("remove directories and their contents recursively")
        )
        .arg(
            // To mimic GNU's behavior we also want the '-R' flag. However, using clap's
            // alias method 'visible_alias("R")' would result in a long '--R' flag.
            Arg::new(OPT_RECURSIVE_R).short('R')
            .help("Equivalent to -r")
        )
        .arg(
            Arg::new(OPT_DIR)
            .short('d')
            .long(OPT_DIR)
            .help("remove empty directories")
        )
        .arg(
            Arg::new(OPT_VERBOSE)
            .short('v')
            .long(OPT_VERBOSE)
            .help("explain what is being done")
        )
        // From the GNU source code:
        // This is solely for testing.
        // Do not document.
        // It is relatively difficult to ensure that there is a tty on stdin.
        // Since rm acts differently depending on that, without this option,
        // it'd be harder to test the parts of rm that depend on that setting.
        // In contrast with Arg::long, Arg::alias does not strip leading
        // hyphens. Therefore it supports 3 leading hyphens.
        .arg(
            Arg::new(PRESUME_INPUT_TTY)
            .long(PRESUME_INPUT_TTY)
            .alias(PRESUME_INPUT_TTY)
            .hide(true)
        )
        .arg(
            Arg::new(ARG_FILES)
            .multiple_occurrences(true)
            .takes_value(true)
            .min_values(1)
            .value_hint(clap::ValueHint::AnyPath)
        )
}

// TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: &[String], options: &Options) -> bool {
    let mut had_err = false;

    for filename in files {
        let file = Path::new(filename);
        had_err = match file.symlink_metadata() {
            Ok(metadata) => {
                if metadata.is_dir() {
                    handle_dir(file, options)
                } else if is_symlink_dir(&metadata) {
                    remove_dir(file, options)
                } else {
                    remove_file(file, options)
                }
            }
            Err(_e) => {
                // TODO: actually print out the specific error
                // TODO: When the error is not about missing files
                // (e.g., permission), even rm -f should fail with
                // outputting the error, but there's no easy eay.
                if !options.force {
                    show_error!(
                        "cannot remove {}: No such file or directory",
                        filename.quote()
                    );
                    true
                } else {
                    false
                }
            }
        }
        .bitor(had_err);
    }

    had_err
}

fn handle_dir(path: &Path, options: &Options) -> bool {
    let mut had_err = false;

    let is_root = path.has_root() && path.parent().is_none();
    if options.recursive && (!is_root || !options.preserve_root) {
        if options.interactive != InteractiveMode::Always && !options.verbose {
            // we need the extra crate because apparently fs::remove_dir_all() does not function
            // correctly on Windows
            if let Err(e) = remove_dir_all(path) {
                had_err = true;
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    // here, GNU doesn't use some kind of remove_dir_all
                    // It will show directory+file
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
            }
        } else {
            let mut dirs: VecDeque<DirEntry> = VecDeque::new();

            for entry in WalkDir::new(path) {
                match entry {
                    Ok(entry) => {
                        let file_type = entry.file_type();
                        if file_type.is_dir() {
                            dirs.push_back(entry);
                        } else {
                            had_err = remove_file(entry.path(), options).bitor(had_err);
                        }
                    }
                    Err(e) => {
                        had_err = true;
                        show_error!("recursing in {}: {}", path.quote(), e);
                    }
                }
            }

            for dir in dirs.iter().rev() {
                had_err = remove_dir(dir.path(), options).bitor(had_err);
            }
        }
    } else if options.dir && (!is_root || !options.preserve_root) {
        had_err = remove_dir(path, options).bitor(had_err);
    } else if options.recursive {
        show_error!("could not remove directory {}", path.quote());
        had_err = true;
    } else {
        show_error!(
            "cannot remove {}: Is a directory", // GNU's rm error message does not include help
            path.quote()
        );
        had_err = true;
    }

    had_err
}

fn remove_dir(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, true)
    } else {
        true
    };
    if response {
        if let Ok(mut read_dir) = fs::read_dir(path) {
            if options.dir || options.recursive {
                if read_dir.next().is_none() {
                    match fs::remove_dir(path) {
                        Ok(_) => {
                            if options.verbose {
                                println!("removed directory {}", normalize(path).quote());
                            }
                        }
                        Err(e) => {
                            if e.kind() == std::io::ErrorKind::PermissionDenied {
                                // GNU compatibility (rm/fail-eacces.sh)
                                show_error!(
                                    "cannot remove {}: {}",
                                    path.quote(),
                                    "Permission denied"
                                );
                            } else {
                                show_error!("cannot remove {}: {}", path.quote(), e);
                            }
                            return true;
                        }
                    }
                } else {
                    // directory can be read but is not empty
                    show_error!("cannot remove {}: Directory not empty", path.quote());
                    return true;
                }
            } else {
                // called to remove a symlink_dir (windows) without "-r"/"-R" or "-d"
                show_error!("cannot remove {}: Is a directory", path.quote());
                return true;
            }
        } else {
            // GNU's rm shows this message if directory is empty but not readable
            show_error!("cannot remove {}: Directory not empty", path.quote());
            return true;
        }
    }

    false
}

fn remove_file(path: &Path, options: &Options) -> bool {
    let response = if options.interactive == InteractiveMode::Always {
        prompt_file(path, false)
    } else {
        true
    };
    if response {
        match fs::remove_file(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed {}", normalize(path).quote());
                }
            }
            Err(e) => {
                if e.kind() == std::io::ErrorKind::PermissionDenied {
                    // GNU compatibility (rm/fail-eacces.sh)
                    show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
                } else {
                    show_error!("cannot remove {}: {}", path.quote(), e);
                }
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory {}? ", path.quote())))
    } else {
        prompt(&(format!("rm: remove file {}? ", path.quote())))
    }
}

fn normalize(path: &Path) -> PathBuf {
    // copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
    // both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
    // for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
    // TODO: replace this once that lands
    uucore::fs::normalize_path(path)
}

fn prompt(msg: &str) -> bool {
    let _ = stderr().write_all(msg.as_bytes());
    let _ = stderr().flush();

    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();

    match stdin.read_until(b'\n', &mut buf) {
        Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'),
        _ => false,
    }
}

#[cfg(not(windows))]
fn is_symlink_dir(_metadata: &fs::Metadata) -> bool {
    false
}

#[cfg(windows)]
use std::os::windows::prelude::MetadataExt;

#[cfg(windows)]
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
    use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;

    metadata.file_type().is_symlink()
        && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! `ShiftAnd` algorithm for pattern matching.
//! Patterns may contain at most 64 symbols.
//! Complexity: O(n) with text length n.
//!
//! # Example
//!
//! ```rust
//! use bio::pattern_matching::shift_and;
//! let pattern = b"AAAA";
//! let text = b"ACGGCTAGAAAAGGCTAG";
//! let shiftand = shift_and::ShiftAnd::new(pattern);
//! let occ = shiftand.find_all(text).next().unwrap();
//! assert_eq!(occ, 8);
//! ```

use std::borrow::Borrow;
use std::iter::Enumerate;

/// `ShiftAnd` algorithm.
pub struct ShiftAnd {
    m: usize,
    masks: [u64; 256],
    accept: u64,
}

impl ShiftAnd {
    /// Create new ShiftAnd instance from a given pattern.
    pub fn new<C, P>(pattern: P) -> Self
    where
        P::IntoIter: ExactSizeIterator,
        C: Borrow<u8>,
        P: IntoIterator<Item = C>,
    {
        let pattern = pattern.into_iter();
        let m = pattern.len();
        assert!(m <= 64, "Expecting a pattern of at most 64 symbols.");
        let (masks, accept) = masks(pattern);

        ShiftAnd { m, masks, accept }
    }

    /// Find all matches of pattern in the given text. Matches are returned as an iterator
    /// over start positions.
    pub fn find_all<C, T>(&self, text: T) -> Matches<'_, C, T::IntoIter>
    where
        C: Borrow<u8>,
        T: IntoIterator<Item = C>,
    {
        Matches {
            shiftand: self,
            active: 0,
            text: text.into_iter().enumerate(),
        }
    }
}

/// Calculate ShiftAnd masks. This function is called automatically when instantiating
/// a new ShiftAnd for a given pattern.
pub fn masks<C, P>(pattern: P) -> ([u64; 256], u64)
where
    C: Borrow<u8>,
    P: IntoIterator<Item = C>,
{
    let mut masks = [0; 256];

    let mut bit = 1;
    for c in pattern {
        masks[*c.borrow() as usize] |= bit;
        bit *= 2;
    }

    (masks, bit / 2)
}

/// Iterator over start positions of matches.
pub struct Matches<'a, C, T>
where
    C: Borrow<u8>,
    T: Iterator<Item = C>,
{
    shiftand: &'a ShiftAnd,
    active: u64,
    text: Enumerate<T>,
}

impl<'a, C, T> Iterator for Matches<'a, C, T>
where
    C: Borrow<u8>,
    T: Iterator<Item = C>,
{
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        for (i, c) in self.text.by_ref() {
            self.active = ((self.active << 1) | 1) & self.shiftand.masks[*c.borrow() as usize];
            if self.active & self.shiftand.accept > 0 {
                return Some(i - self.shiftand.m + 1);
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use itertools::Itertools;

    #[test]
    fn test_find_all() {
        let text = b"dhjalkjwqnnnannanaflkjdklfj";
        let pattern = b"qnnnannan";
        let shiftand = ShiftAnd::new(pattern);
        assert_eq!(shiftand.find_all(text).collect_vec(), [8]);
    }
}
<commit_msg>Fix pattern matching subtract with overflow panic (#417)<commit_after>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! `ShiftAnd` algorithm for pattern matching.
//! Patterns may contain at most 64 symbols.
//! Complexity: O(n) with text length n.
//!
//! # Example
//!
//! ```rust
//! use bio::pattern_matching::shift_and;
//! let pattern = b"AAAA";
//! let text = b"ACGGCTAGAAAAGGCTAG";
//! let shiftand = shift_and::ShiftAnd::new(pattern);
//! let occ = shiftand.find_all(text).next().unwrap();
//! assert_eq!(occ, 8);
//! ```

use std::borrow::Borrow;
use std::iter::Enumerate;

/// `ShiftAnd` algorithm.
pub struct ShiftAnd {
    m: usize,
    masks: [u64; 256],
    accept: u64,
}

impl ShiftAnd {
    /// Create new ShiftAnd instance from a given pattern.
    pub fn new<C, P>(pattern: P) -> Self
    where
        P::IntoIter: ExactSizeIterator,
        C: Borrow<u8>,
        P: IntoIterator<Item = C>,
    {
        let pattern = pattern.into_iter();
        let m = pattern.len();
        assert!(m <= 64, "Expecting a pattern of at most 64 symbols.");
        let (masks, accept) = masks(pattern);

        ShiftAnd { m, masks, accept }
    }

    /// Find all matches of pattern in the given text. Matches are returned as an iterator
    /// over start positions.
    pub fn find_all<C, T>(&self, text: T) -> Matches<'_, C, T::IntoIter>
    where
        C: Borrow<u8>,
        T: IntoIterator<Item = C>,
    {
        Matches {
            shiftand: self,
            active: 0,
            text: text.into_iter().enumerate(),
        }
    }
}

/// Calculate ShiftAnd masks. This function is called automatically when instantiating
/// a new ShiftAnd for a given pattern.
pub fn masks<C, P>(pattern: P) -> ([u64; 256], u64)
where
    C: Borrow<u8>,
    P: IntoIterator<Item = C>,
{
    let mut masks = [0; 256];

    let mut bit = 1;
    for c in pattern {
        masks[*c.borrow() as usize] |= bit;
        bit *= 2;
    }

    (masks, bit / 2)
}

/// Iterator over start positions of matches.
pub struct Matches<'a, C, T>
where
    C: Borrow<u8>,
    T: Iterator<Item = C>,
{
    shiftand: &'a ShiftAnd,
    active: u64,
    text: Enumerate<T>,
}

impl<'a, C, T> Iterator for Matches<'a, C, T>
where
    C: Borrow<u8>,
    T: Iterator<Item = C>,
{
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        for (i, c) in self.text.by_ref() {
            self.active = ((self.active << 1) | 1) & self.shiftand.masks[*c.borrow() as usize];
            if self.active & self.shiftand.accept > 0 {
                return Some(i + 1 - self.shiftand.m);
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use itertools::Itertools;

    #[test]
    fn test_find_all() {
        let text = b"dhjalkjwqnnnannanaflkjdklfj";
        let pattern = b"qnnnannan";
        let shiftand = ShiftAnd::new(pattern);
        assert_eq!(shiftand.find_all(text).collect_vec(), [8]);
    }

    #[test]
    fn test_issue_416() {
        let text_pos_0 = b"CCTTTTTTTTTTTTTTT";
        let pattern = b"CC";
        let shiftand = ShiftAnd::new(pattern);
        assert_eq!(shiftand.find_all(text_pos_0).collect_vec(), [0]);
    }
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2018 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use ethabi;
use rustc_hex::FromHex;

use cita_crypto::PrivKey;
use cita_types::{H160, U256};
use cita_types::traits::LowerHex;
use libproto::blockchain::{Transaction, UnverifiedTransaction};

pub fn construct_transaction(
    pkey: &PrivKey,
    tx_proof_rlp: Vec<u8>,
    dest_hasher: &str,
    dest_contract: H160,
    chain_id: u32,
    height: U256,
) -> Option<UnverifiedTransaction> {
    encode(dest_hasher, tx_proof_rlp).map(|code| sign(pkey, dest_contract, code, chain_id, height))
}

#[inline]
fn encode(dest_hasher: &str, tx_proof_rlp: Vec<u8>) -> Option<Vec<u8>> {
    FromHex::from_hex(dest_hasher)
        .map(|hasher| {
            trace!("encode dest_hasher {:?}", hasher);
            trace!("encode proof_len {:?}", tx_proof_rlp.len());
            trace!("encode proof_data {:?}", tx_proof_rlp);
            let encoded = ethabi::encode(&[ethabi::Token::Bytes(tx_proof_rlp)]);
            let ret = hasher.into_iter().chain(encoded.into_iter()).collect();
            trace!("encode result {:?}", ret);
            ret
        })
        .ok()
}

#[inline]
fn sign(pkey: &PrivKey, addr: H160, code: Vec<u8>, chain_id: u32, height: U256) -> UnverifiedTransaction {
    let mut tx = Transaction::new();
    tx.set_data(code);
    tx.set_to(addr.lower_hex());
    tx.set_valid_until_block(height.low_u64() + 100);
    tx.set_quota(10000000000);
    tx.set_chain_id(chain_id);
    tx.sign(*pkey).take_transaction_with_sig()
}
<commit_msg>set smaller quota<commit_after>// CITA
// Copyright 2016-2018 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use ethabi;
use rustc_hex::FromHex;

use cita_crypto::PrivKey;
use cita_types::{H160, U256};
use cita_types::traits::LowerHex;
use libproto::blockchain::{Transaction, UnverifiedTransaction};

pub fn construct_transaction(
    pkey: &PrivKey,
    tx_proof_rlp: Vec<u8>,
    dest_hasher: &str,
    dest_contract: H160,
    chain_id: u32,
    height: U256,
) -> Option<UnverifiedTransaction> {
    encode(dest_hasher, tx_proof_rlp).map(|code| sign(pkey, dest_contract, code, chain_id, height))
}

#[inline]
fn encode(dest_hasher: &str, tx_proof_rlp: Vec<u8>) -> Option<Vec<u8>> {
    FromHex::from_hex(dest_hasher)
        .map(|hasher| {
            trace!("encode dest_hasher {:?}", hasher);
            trace!("encode proof_len {:?}", tx_proof_rlp.len());
            trace!("encode proof_data {:?}", tx_proof_rlp);
            let encoded = ethabi::encode(&[ethabi::Token::Bytes(tx_proof_rlp)]);
            let ret = hasher.into_iter().chain(encoded.into_iter()).collect();
            trace!("encode result {:?}", ret);
            ret
        })
        .ok()
}

#[inline]
fn sign(pkey: &PrivKey, addr: H160, code: Vec<u8>, chain_id: u32, height: U256) -> UnverifiedTransaction {
    let mut tx = Transaction::new();
    tx.set_data(code);
    tx.set_to(addr.lower_hex());
    tx.set_valid_until_block(height.low_u64() + 100);
    tx.set_quota(1000000);
    tx.set_chain_id(chain_id);
    tx.sign(*pkey).take_transaction_with_sig()
}
<|endoftext|>


<commit_before>use hyper::Client;
use super::helper::KinesisHelper;
use utils::screen::Screen;
use utils::screen::Status;

use rusoto::{
    DefaultCredentialsProvider,
    Region
};

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    RecordList(String),
    Record(String, String), // itrator_id, record
}

pub fn run(credential_provider: DefaultCredentialsProvider, client: Client, region: Region) {

    let kinesis_helper = KinesisHelper::new(client, credential_provider, region);
    let mut screen: Screen = Screen::new();
    let mut state: State = State::Root;

    loop {
        state = match state {
            State::Root                           => root(&screen, &kinesis_helper),
            State::StreamList(streams)            => stream_list(&screen, &kinesis_helper, streams),
            State::ShardList(stream_name, shards) => shared_list(&screen, &kinesis_helper, stream_name, shards),
            State::RecordList(shard_iterator)     => record_list(&screen, &kinesis_helper, shard_iterator),
            State::Record(shard_iterator, record) => record(&screen, &kinesis_helper, shard_iterator, record),
            State::End => {
                break;
            }
        };
    }
}

fn root(screen: &mut Screen, kinesis_helper: &KinesisHelper) -> State {

    let commands = vec!["l".to_string(), "q".to_string()];
    screen.update_screen("🍓  Kinesis", &commands);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(ref c) if c == "l" => {
            match kinesis_helper.list_streams() {
                Ok(streams) => State::StreamList(streams),
                Err(e) =>  State::Root
            }
        }
        _ => State::Root
    }
}

fn stream_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, streams: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams", &streams);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(stream_name) => {
            match kinesis_helper.describe_shards(&stream_name) {
                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }
}

fn shared_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, stream_name: &str, shards: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams > Shards", &shards);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(shard_id) => {
            match kinesis_helper.get_shard_iterator(&stream_name, &shard_id) {
                Ok(shard_iterator) => State::RecordList(shard_iterator),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }

}

fn record_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String) -> State {

    match kinesis_helper.get_records(&shard_iterator) {

        Ok((results, iterator)) => {

            screen.update_screen("🍓  Kinesis > Streams > Shards > Records",
                                 &kinesis_helper.decode_records(&results));

            match screen.select_line() {
                Status::Error | Status::Quit => State::End,
                Status::Selected(ref c) if c == "n" => {
                    match iterator {
                        Some(iterator) => State::RecordList(iterator),
                        None => State::Root
                    }
                },
                Status::Selected(ref record) => State::Record(&shard_iterator, record.to_string()),
                _ => State::Root
            }
        },
        Err(e) => State::Root,
    }
}

fn record(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String, record: String) -> State {
    screen.update_screen("🍓  Kinesis > Streams > Shards > Records > Record", &vec!(String::new()));

    match screen.render(&kinesis_helper.format_record(&record)) {
        Status::Error | Status::Quit => State::End,
        Status::Escaped  => State::RecordList(shard_iterator),
        _ => State::Root
    }
}
<commit_msg>Remove comment<commit_after>use hyper::Client;
use super::helper::KinesisHelper;
use utils::screen::Screen;
use utils::screen::Status;
use rusoto::{
    DefaultCredentialsProvider,
    Region
};

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    RecordList(String),
    Record(String, String),
}

pub fn run(credential_provider: DefaultCredentialsProvider, client: Client, region: Region) {

    let kinesis_helper = KinesisHelper::new(client, credential_provider, region);
    let mut screen: Screen = Screen::new();
    let mut state: State = State::Root;

    loop {
        state = match state {
            State::Root                           => root(&screen, &kinesis_helper),
            State::StreamList(streams)            => stream_list(&screen, &kinesis_helper, streams),
            State::ShardList(stream_name, shards) => shared_list(&screen, &kinesis_helper, stream_name, shards),
            State::RecordList(shard_iterator)     => record_list(&screen, &kinesis_helper, shard_iterator),
            State::Record(shard_iterator, record) => record(&screen, &kinesis_helper, shard_iterator, record),
            State::End => {
                break;
            }
        };
    }
}

fn root(screen: &mut Screen, kinesis_helper: &KinesisHelper) -> State {

    let commands = vec!["l".to_string(), "q".to_string()];
    screen.update_screen("🍓  Kinesis", &commands);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(ref c) if c == "l" => {
            match kinesis_helper.list_streams() {
                Ok(streams) => State::StreamList(streams),
                Err(e) =>  State::Root
            }
        }
        _ => State::Root
    }
}

fn stream_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, streams: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams", &streams);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(stream_name) => {
            match kinesis_helper.describe_shards(&stream_name) {
                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }
}

fn shared_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, stream_name: &str, shards: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams > Shards", &shards);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(shard_id) => {
            match kinesis_helper.get_shard_iterator(&stream_name, &shard_id) {
                Ok(shard_iterator) => State::RecordList(shard_iterator),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }

}

fn record_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String) -> State {

    match kinesis_helper.get_records(&shard_iterator) {

        Ok((results, iterator)) => {

            screen.update_screen("🍓  Kinesis > Streams > Shards > Records",
                                 &kinesis_helper.decode_records(&results));

            match screen.select_line() {
                Status::Error | Status::Quit => State::End,
                Status::Selected(ref c) if c == "n" => {
                    match iterator {
                        Some(iterator) => State::RecordList(iterator),
                        None => State::Root
                    }
                },
                Status::Selected(ref record) => State::Record(&shard_iterator, record.to_string()),
                _ => State::Root
            }
        },
        Err(e) => State::Root,
    }
}

fn record(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String, record: String) -> State {
    screen.update_screen("🍓  Kinesis > Streams > Shards > Records > Record", &vec!(String::new()));

    match screen.render(&kinesis_helper.format_record(&record)) {
        Status::Error | Status::Quit => State::End,
        Status::Escaped  => State::RecordList(shard_iterator),
        _ => State::Root
    }
}
<|endoftext|>