<commit_before>// Copyright 2012-2018 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.

use std::collections::HashSet;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Read;
use std::path::Path;

macro_rules! try_something {
    ($e:expr, $out:expr) => ({
        match $e {
            Ok(c) => c,
            Err(e) => {
                eprintln!("rustdoc: got an error: {}", e);
                return $out;
            }
        }
    })
}

#[derive(Debug, Clone, Eq)]
pub struct CssPath {
    pub name: String,
    pub children: HashSet<CssPath>,
}

// This PartialEq implementation IS NOT COMMUTATIVE!!!
//
// The order is very important: the second object must have all first's rules.
// However, the first doesn't require to have all second's rules.
impl PartialEq for CssPath {
    fn eq(&self, other: &CssPath) -> bool {
        if self.name != other.name {
            false
        } else {
            for child in &self.children {
                if !other.children.iter().any(|c| child == c) {
                    return false;
                }
            }
            true
        }
    }
}

impl Hash for CssPath {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        for x in &self.children {
            x.hash(state);
        }
    }
}

impl CssPath {
    fn new(name: String) -> CssPath {
        CssPath {
            name,
            children: HashSet::new(),
        }
    }
}

/// All variants contain the position they occur.
#[derive(Debug, Clone, Copy)]
enum Events {
    StartLineComment(usize),
    StartComment(usize),
    EndComment(usize),
    InBlock(usize),
    OutBlock(usize),
}

impl Events {
    fn get_pos(&self) -> usize {
        match *self {
            Events::StartLineComment(p) |
            Events::StartComment(p) |
            Events::EndComment(p) |
            Events::InBlock(p) |
            Events::OutBlock(p) => p,
        }
    }

    fn is_comment(&self) -> bool {
        match *self {
            Events::StartLineComment(_) |
            Events::StartComment(_) |
            Events::EndComment(_) => true,
            _ => false,
        }
    }
}

fn previous_is_line_comment(events: &[Events]) -> bool {
    if let Some(&Events::StartLineComment(_)) = events.last() {
        true
    } else {
        false
    }
}

fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
    if let Some(&Events::StartComment(_)) = events.last() {
        return false;
    }
    pos + 1 < v.len() && v[pos + 1] == b'/'
}

fn load_css_events(v: &[u8]) -> Vec<Events> {
    let mut pos = 0;
    let mut events = Vec::with_capacity(100);

    while pos < v.len() - 1 {
        match v[pos] {
            b'/' if pos + 1 < v.len() && v[pos + 1] == b'*' => {
                events.push(Events::StartComment(pos));
                pos += 1;
            }
            b'/' if is_line_comment(pos, v, &events) => {
                events.push(Events::StartLineComment(pos));
                pos += 1;
            }
            b'\n' if previous_is_line_comment(&events) => {
                events.push(Events::EndComment(pos));
            }
            b'*' if pos + 1 < v.len() && v[pos + 1] == b'/' => {
                events.push(Events::EndComment(pos + 2));
                pos += 1;
            }
            b'{' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue
                }
                events.push(Events::InBlock(pos + 1));
            }
            b'}' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue
                }
                events.push(Events::OutBlock(pos + 1));
            }
            _ => {}
        }
        pos += 1;
    }
    events
}

fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
    while *pos < events.len() {
        if !events[*pos].is_comment() {
            return Some(events[*pos]);
        }
        *pos += 1;
    }
    None
}

fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet<CssPath> {
    let mut pathes = Vec::with_capacity(50);

    while *pos < events.len() {
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            println!("00 => {:?}", events[*pos]);
            *pos += 1;
            break
        }
        println!("a => {:?}", events[*pos]);
        if let Some(Events::InBlock(start_pos)) = get_useful_next(events, pos) {
            println!("aa => {:?}", events[*pos]);
            pathes.push(CssPath::new(::std::str::from_utf8(if *pos > 0 {
                &v[events[*pos - 1].get_pos()..start_pos - 1]
            } else {
                &v[..start_pos]
            }).unwrap_or("").trim().to_owned()));
            *pos += 1;
        }
        println!("b => {:?}", events[*pos]);
        while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            println!("bb => {:?}", events[*pos]);
            if let Some(ref mut path) = pathes.last_mut() {
                for entry in inner(v, events, pos).iter() {
                    path.children.insert(entry.clone());
                }
            }
        }
        if *pos < events.len() {
            println!("c => {:?}", events[*pos]);
        }
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
        }
    }
    pathes.iter().cloned().collect()
}

pub fn load_css_pathes(v: &[u8]) -> CssPath {
    let events = load_css_events(v);
    let mut pos = 0;

    println!("\n======> {:?}", events);
    let mut parent = CssPath::new("parent".to_owned());
    parent.children = inner(v, &events, &mut pos);
    parent
}

fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
    if against.name != other.name {
        return
    } else {
        for child in &against.children {
            let mut found = false;
            let mut found_working = false;
            let mut tmp = Vec::new();

            for other_child in &other.children {
                if child.name == other_child.name {
                    if child != other_child {
                        get_differences(child, other_child, &mut tmp);
                    } else {
                        found_working = true;
                    }
                    found = true;
                    break
                }
            }
            if found == false {
                v.push(format!("  Missing \"{}\" rule", child.name));
            } else if found_working == false {
                v.extend(tmp.iter().cloned());
            }
        }
    }
}

pub fn test_theme_against<P: AsRef<Path>>(f: &P, against: &CssPath) -> Vec<String> {
    let mut file = try_something!(File::open(f), Vec::new());
    let mut data = Vec::with_capacity(1000);

    try_something!(file.read_to_end(&mut data), Vec::new());
    let pathes = load_css_pathes(&data);
    println!("========= {:?}", pathes);
    println!("========= {:?}", against);
    let mut ret = Vec::new();
    get_differences(against, &pathes, &mut ret);
    ret
}

/*#[test]
fn test_comments_in_rules() {
    let text = r#"
rule a {}

rule b, c
// a line comment
{}

rule d
// another line comment
e {}

rule f/* a multine

comment*/{}

rule g/* another multine

comment*/h

i {}

rule j/*commeeeeent

you like things like "{}" in there? :)
*/
end {}
"#;
}*/
<commit_msg>Handle comments in css selector and add tests<commit_after>// Copyright 2012-2018 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.

use std::collections::HashSet;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Read;
use std::path::Path;

macro_rules! try_something {
    ($e:expr, $out:expr) => ({
        match $e {
            Ok(c) => c,
            Err(e) => {
                eprintln!("rustdoc: got an error: {}", e);
                return $out;
            }
        }
    })
}

#[derive(Debug, Clone, Eq)]
pub struct CssPath {
    pub name: String,
    pub children: HashSet<CssPath>,
}

// This PartialEq implementation IS NOT COMMUTATIVE!!!
//
// The order is very important: the second object must have all first's rules.
// However, the first doesn't require to have all second's rules.
impl PartialEq for CssPath {
    fn eq(&self, other: &CssPath) -> bool {
        if self.name != other.name {
            false
        } else {
            for child in &self.children {
                if !other.children.iter().any(|c| child == c) {
                    return false;
                }
            }
            true
        }
    }
}

impl Hash for CssPath {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        for x in &self.children {
            x.hash(state);
        }
    }
}

impl CssPath {
    fn new(name: String) -> CssPath {
        CssPath {
            name,
            children: HashSet::new(),
        }
    }
}

/// All variants contain the position they occur.
#[derive(Debug, Clone, Copy)]
enum Events {
    StartLineComment(usize),
    StartComment(usize),
    EndComment(usize),
    InBlock(usize),
    OutBlock(usize),
}

impl Events {
    fn get_pos(&self) -> usize {
        match *self {
            Events::StartLineComment(p) |
            Events::StartComment(p) |
            Events::EndComment(p) |
            Events::InBlock(p) |
            Events::OutBlock(p) => p,
        }
    }

    fn is_comment(&self) -> bool {
        match *self {
            Events::StartLineComment(_) |
            Events::StartComment(_) |
            Events::EndComment(_) => true,
            _ => false,
        }
    }
}

fn previous_is_line_comment(events: &[Events]) -> bool {
    if let Some(&Events::StartLineComment(_)) = events.last() {
        true
    } else {
        false
    }
}

fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
    if let Some(&Events::StartComment(_)) = events.last() {
        return false;
    }
    pos + 1 < v.len() && v[pos + 1] == b'/'
}

fn load_css_events(v: &[u8]) -> Vec<Events> {
    let mut pos = 0;
    let mut events = Vec::with_capacity(100);

    while pos < v.len() - 1 {
        match v[pos] {
            b'/' if pos + 1 < v.len() && v[pos + 1] == b'*' => {
                events.push(Events::StartComment(pos));
                pos += 1;
            }
            b'/' if is_line_comment(pos, v, &events) => {
                events.push(Events::StartLineComment(pos));
                pos += 1;
            }
            b'\n' if previous_is_line_comment(&events) => {
                events.push(Events::EndComment(pos));
            }
            b'*' if pos + 1 < v.len() && v[pos + 1] == b'/' => {
                events.push(Events::EndComment(pos + 2));
                pos += 1;
            }
            b'{' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue
                }
                events.push(Events::InBlock(pos + 1));
            }
            b'}' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue
                }
                events.push(Events::OutBlock(pos + 1));
            }
            _ => {}
        }
        pos += 1;
    }
    events
}

fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
    while *pos < events.len() {
        if !events[*pos].is_comment() {
            return Some(events[*pos]);
        }
        *pos += 1;
    }
    None
}

fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
    let mut ret = Vec::with_capacity(3);

    ret.push(events[pos].get_pos() - 1);
    if pos > 0 {
        pos -= 1;
    }
    loop {
        ret.push(events[pos].get_pos());
        if pos < 1 || !events[pos].is_comment() {
            break
        }
        pos -= 1;
    }
    if events[pos].is_comment() {
        ret.push(0);
    }
    ret.iter().rev().cloned().collect()
}

fn build_rule(v: &[u8], positions: &[usize]) -> String {
    positions.chunks(2)
             .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("").to_owned())
             .collect::<String>()
             .trim()
             .replace("\n", " ")
}

fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet<CssPath> {
    let mut pathes = Vec::with_capacity(50);

    while *pos < events.len() {
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
            break
        }
        if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            pathes.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
            *pos += 1;
        }
        while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            if let Some(ref mut path) = pathes.last_mut() {
                for entry in inner(v, events, pos).iter() {
                    path.children.insert(entry.clone());
                }
            }
        }
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
        }
    }
    pathes.iter().cloned().collect()
}

pub fn load_css_pathes(v: &[u8]) -> CssPath {
    let events = load_css_events(v);
    let mut pos = 0;

    let mut parent = CssPath::new("parent".to_owned());
    parent.children = inner(v, &events, &mut pos);
    parent
}

pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
    if against.name != other.name {
        return
    } else {
        for child in &against.children {
            let mut found = false;
            let mut found_working = false;
            let mut tmp = Vec::new();

            for other_child in &other.children {
                if child.name == other_child.name {
                    if child != other_child {
                        get_differences(child, other_child, &mut tmp);
                    } else {
                        found_working = true;
                    }
                    found = true;
                    break
                }
            }
            if found == false {
                v.push(format!("  Missing \"{}\" rule", child.name));
            } else if found_working == false {
                v.extend(tmp.iter().cloned());
            }
        }
    }
}

pub fn test_theme_against<P: AsRef<Path>>(f: &P, against: &CssPath) -> Vec<String> {
    let mut file = try_something!(File::open(f), Vec::new());
    let mut data = Vec::with_capacity(1000);

    try_something!(file.read_to_end(&mut data), Vec::new());
    let pathes = load_css_pathes(&data);
    let mut ret = Vec::new();
    get_differences(against, &pathes, &mut ret);
    ret
}

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

    #[test]
    fn test_comments_in_rules() {
        let text = r#"
rule a {}

rule b, c
// a line comment
{}

rule d
// another line comment
e {}

rule f/* a multine

comment*/{}

rule g/* another multine

comment*/h

i {}

rule j/*commeeeeent

you like things like "{}" in there? :)
*/
end {}"#;

        let against = r#"
rule a {}

rule b, c {}

rule d e {}

rule f {}

rule gh i {}

rule j end {}
"#;

        assert!(get_differences(&load_css_pathes(against.as_bytes()),
                                &load_css_pathes(text.as_bytes())).is_empty());
    }

    #[test]
    fn test_comparison() {
        let x = r#"
a {
    b {
        c {}
    }
}
"#;

        let y = r#"
a {
    b {}
}
"#;

        let against = load_css_pathes(y.as_bytes());
        let other = load_css_pathes(x.as_bytes());

        assert!(get_differences(&against, &other).is_empty());
        assert_eq!(get_differences(&other, &against), vec!["  Missing \"c\" rule".to_owned()])
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

/*!

Signal handling

This modules provides bindings to receive signals safely, built on top of the
local I/O factory. There are a number of defined signals which can be caught,
but not all signals will work across all platforms (windows doesn't have
definitions for a number of signals.

*/

use clone::Clone;
use comm::{Sender, Receiver, channel};
use io;
use iter::Iterator;
use mem::drop;
use option::{Some, None};
use result::{Ok, Err};
use rt::rtio::{IoFactory, LocalIo, RtioSignal};
use slice::{ImmutableVector, OwnedVector};

/// Signals that can be sent and received
#[repr(int)]
#[deriving(Eq, Hash, Show)]
pub enum Signum {
    /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
    Break = 21i,
    /// Equivalent to SIGHUP, delivered when the user closes the terminal
    /// window. On delivery of HangUp, the program is given approximately
    /// 10 seconds to perform any cleanup. After that, Windows will
    /// unconditionally terminate it.
    HangUp = 1i,
    /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
    Interrupt = 2i,
    /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
    Quit = 3i,
    /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
    StopTemporarily = 20i,
    /// Equivalent to SIGUSR1.
    User1 = 10i,
    /// Equivalent to SIGUSR2.
    User2 = 12i,
    /// Equivalent to SIGWINCH, delivered when the console has been resized.
    /// WindowSizeChange may not be delivered in a timely manner; size change
    /// will only be detected when the cursor is being moved.
    WindowSizeChange = 28i,
}

/// Listener provides a receiver to listen for registered signals.
///
/// Listener automatically unregisters its handles once it is out of scope.
/// However, clients can still unregister signums manually.
///
/// # Example
///
/// ```rust,ignore
/// use std::io::signal::{Listener, Interrupt};
///
/// let mut listener = Listener::new();
/// listener.register(Interrupt);
///
/// spawn({
///     loop {
///         match listener.rx.recv() {
///             Interrupt => println!("Got Interrupt'ed"),
///             _ => (),
///         }
///     }
/// });
///
/// ```
pub struct Listener {
    /// A map from signums to handles to keep the handles in memory
    handles: ~[(Signum, ~RtioSignal)],
    /// This is where all the handles send signums, which are received by
    /// the clients from the receiver.
    tx: Sender<Signum>,

    /// Clients of Listener can `recv()` on this receiver. This is exposed to
    /// allow selection over it as well as manipulation of the receiver
    /// directly.
    pub rx: Receiver<Signum>,
}

impl Listener {
    /// Creates a new listener for signals. Once created, signals are bound via
    /// the `register` method (otherwise nothing will ever be received)
    pub fn new() -> Listener {
        let (tx, rx) = channel();
        Listener {
            tx: tx,
            rx: rx,
            handles: ~[],
        }
    }

    /// Listen for a signal, returning true when successfully registered for
    /// signum. Signals can be received using `recv()`.
    ///
    /// Once a signal is registered, this listener will continue to receive
    /// notifications of signals until it is unregistered. This occurs
    /// regardless of the number of other listeners registered in other tasks
    /// (or on this task).
    ///
    /// Signals are still received if there is no task actively waiting for
    /// a signal, and a later call to `recv` will return the signal that was
    /// received while no task was waiting on it.
    ///
    /// # Error
    ///
    /// If this function fails to register a signal handler, then an error will
    /// be returned.
    pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
        if self.handles.iter().any(|&(sig, _)| sig == signum) {
            return Ok(()); // self is already listening to signum, so succeed
        }
        match LocalIo::maybe_raise(|io| {
            io.signal(signum, self.tx.clone())
        }) {
            Ok(handle) => {
                self.handles.push((signum, handle));
                Ok(())
            }
            Err(e) => Err(e)
        }
    }

    /// Unregisters a signal. If this listener currently had a handler
    /// registered for the signal, then it will stop receiving any more
    /// notification about the signal. If the signal has already been received,
    /// it may still be returned by `recv`.
    pub fn unregister(&mut self, signum: Signum) {
        match self.handles.iter().position(|&(i, _)| i == signum) {
            Some(i) => drop(self.handles.remove(i)),
            None => {}
        }
    }
}

#[cfg(test, unix)]
mod test_unix {
    use libc;
    use comm::Empty;
    use io::timer;
    use super::{Listener, Interrupt};

    fn sigint() {
        unsafe {
            libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_smoketest() {
        let mut signal = Listener::new();
        signal.register(Interrupt).unwrap();
        sigint();
        timer::sleep(10);
        match signal.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_two_signal_one_signum() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt).unwrap();
        s2.register(Interrupt).unwrap();
        sigint();
        timer::sleep(10);
        match s1.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
        match s2.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_unregister() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt).unwrap();
        s2.register(Interrupt).unwrap();
        s2.unregister(Interrupt);
        sigint();
        timer::sleep(10);
        assert_eq!(s2.rx.try_recv(), Empty);
    }
}

#[cfg(test, windows)]
mod test_windows {
    use super::{User1, Listener};
    use result::{Ok, Err};

    #[test]
    fn test_io_signal_invalid_signum() {
        let mut s = Listener::new();
        match s.register(User1) {
            Ok(..) => {
                fail!("Unexpected successful registry of signum {:?}", User1);
            }
            Err(..) => {}
        }
    }
}
<commit_msg>std: Fix a doc example on io::signal<commit_after>// Copyright 2013 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.

/*!

Signal handling

This modules provides bindings to receive signals safely, built on top of the
local I/O factory. There are a number of defined signals which can be caught,
but not all signals will work across all platforms (windows doesn't have
definitions for a number of signals.

*/

use clone::Clone;
use comm::{Sender, Receiver, channel};
use io;
use iter::Iterator;
use kinds::Send;
use mem::drop;
use option::{Some, None};
use result::{Ok, Err};
use rt::rtio::{IoFactory, LocalIo, RtioSignal};
use slice::{ImmutableVector, OwnedVector};

/// Signals that can be sent and received
#[repr(int)]
#[deriving(Eq, Hash, Show)]
pub enum Signum {
    /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
    Break = 21i,
    /// Equivalent to SIGHUP, delivered when the user closes the terminal
    /// window. On delivery of HangUp, the program is given approximately
    /// 10 seconds to perform any cleanup. After that, Windows will
    /// unconditionally terminate it.
    HangUp = 1i,
    /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
    Interrupt = 2i,
    /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
    Quit = 3i,
    /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
    StopTemporarily = 20i,
    /// Equivalent to SIGUSR1.
    User1 = 10i,
    /// Equivalent to SIGUSR2.
    User2 = 12i,
    /// Equivalent to SIGWINCH, delivered when the console has been resized.
    /// WindowSizeChange may not be delivered in a timely manner; size change
    /// will only be detected when the cursor is being moved.
    WindowSizeChange = 28i,
}

/// Listener provides a receiver to listen for registered signals.
///
/// Listener automatically unregisters its handles once it is out of scope.
/// However, clients can still unregister signums manually.
///
/// # Example
///
/// ```rust,no_run
/// # #![allow(unused_must_use)]
/// use std::io::signal::{Listener, Interrupt};
///
/// let mut listener = Listener::new();
/// listener.register(Interrupt);
///
/// loop {
///     match listener.rx.recv() {
///         Interrupt => println!("Got Interrupt'ed"),
///         _ => (),
///     }
/// }
/// ```
pub struct Listener {
    /// A map from signums to handles to keep the handles in memory
    handles: ~[(Signum, ~RtioSignal:Send)],
    /// This is where all the handles send signums, which are received by
    /// the clients from the receiver.
    tx: Sender<Signum>,

    /// Clients of Listener can `recv()` on this receiver. This is exposed to
    /// allow selection over it as well as manipulation of the receiver
    /// directly.
    pub rx: Receiver<Signum>,
}

impl Listener {
    /// Creates a new listener for signals. Once created, signals are bound via
    /// the `register` method (otherwise nothing will ever be received)
    pub fn new() -> Listener {
        let (tx, rx) = channel();
        Listener {
            tx: tx,
            rx: rx,
            handles: ~[],
        }
    }

    /// Listen for a signal, returning true when successfully registered for
    /// signum. Signals can be received using `recv()`.
    ///
    /// Once a signal is registered, this listener will continue to receive
    /// notifications of signals until it is unregistered. This occurs
    /// regardless of the number of other listeners registered in other tasks
    /// (or on this task).
    ///
    /// Signals are still received if there is no task actively waiting for
    /// a signal, and a later call to `recv` will return the signal that was
    /// received while no task was waiting on it.
    ///
    /// # Error
    ///
    /// If this function fails to register a signal handler, then an error will
    /// be returned.
    pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
        if self.handles.iter().any(|&(sig, _)| sig == signum) {
            return Ok(()); // self is already listening to signum, so succeed
        }
        match LocalIo::maybe_raise(|io| {
            io.signal(signum, self.tx.clone())
        }) {
            Ok(handle) => {
                self.handles.push((signum, handle));
                Ok(())
            }
            Err(e) => Err(e)
        }
    }

    /// Unregisters a signal. If this listener currently had a handler
    /// registered for the signal, then it will stop receiving any more
    /// notification about the signal. If the signal has already been received,
    /// it may still be returned by `recv`.
    pub fn unregister(&mut self, signum: Signum) {
        match self.handles.iter().position(|&(i, _)| i == signum) {
            Some(i) => drop(self.handles.remove(i)),
            None => {}
        }
    }
}

#[cfg(test, unix)]
mod test_unix {
    use libc;
    use comm::Empty;
    use io::timer;
    use super::{Listener, Interrupt};

    fn sigint() {
        unsafe {
            libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_smoketest() {
        let mut signal = Listener::new();
        signal.register(Interrupt).unwrap();
        sigint();
        timer::sleep(10);
        match signal.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_two_signal_one_signum() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt).unwrap();
        s2.register(Interrupt).unwrap();
        sigint();
        timer::sleep(10);
        match s1.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
        match s2.rx.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_unregister() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt).unwrap();
        s2.register(Interrupt).unwrap();
        s2.unregister(Interrupt);
        sigint();
        timer::sleep(10);
        assert_eq!(s2.rx.try_recv(), Empty);
    }
}

#[cfg(test, windows)]
mod test_windows {
    use super::{User1, Listener};
    use result::{Ok, Err};

    #[test]
    fn test_io_signal_invalid_signum() {
        let mut s = Listener::new();
        match s.register(User1) {
            Ok(..) => {
                fail!("Unexpected successful registry of signum {:?}", User1);
            }
            Err(..) => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::num::FloatMath;
use linalg;
use linalg::Matrix4;
use linalg::Vector;
use linalg::Point;
use linalg::Normal;
use linalg::Ray;

/// Transform describes an affine transformation in 3D space
/// and stores both the transformation and its inverse
#[deriving(Show, Copy, PartialEq)]
pub struct Transform {
    pub mat: Matrix4,
    pub inv: Matrix4,
}

impl Transform {
    /// Construct the identity transformation
    pub fn identity() -> Transform {
        Transform {
            mat: Matrix4::identity(),
            inv: Matrix4::identity(),
        }
    }
    /// Construct a transform from an existing matrix
    pub fn from_mat(mat: &Matrix4) -> Transform {
        Transform { mat: *mat, inv: mat.inverse() }
    }
    /// Construct a transform from an existing matrix/inverse pair
    pub fn from_pair(mat: &Matrix4, inv: &Matrix4) -> Transform {
        Transform { mat: *mat, inv: *inv }
    }
    /// Construct a transformation matrix to translate by the vector
    pub fn translate(v: &Vector) -> Transform {
        Transform {
            mat: Matrix4::new([1f32, 0f32, 0f32, v.x,
                               0f32, 1f32, 0f32, v.y,
                               0f32, 0f32, 1f32, v.z,
                               0f32, 0f32, 0f32, 1f32]),
            inv: Matrix4::new([1f32, 0f32, 0f32, -v.x,
                               0f32, 1f32, 0f32, -v.y,
                               0f32, 0f32, 1f32, -v.z,
                               0f32, 0f32, 0f32, 1f32]),
        }
    }
    /// Construct a transform to scale x, y and z by the values in the vector
    pub fn scale(v: &Vector) -> Transform {
        Transform {
            mat: Matrix4::new([v.x, 0f32, 0f32, 0f32,
                               0f32, v.y, 0f32, 0f32,
                               0f32, 0f32, v.z, 0f32,
                               0f32, 0f32, 0f32, 1f32]),
            inv: Matrix4::new([1f32 / v.x, 0f32, 0f32, 0f32,
                               0f32, 1f32 / v.y, 0f32, 0f32,
                               0f32, 0f32, 1f32 / v.z, 0f32,
                               0f32, 0f32, 0f32, 1f32]),
        }
    }
    /// Construct a transform to rotate `deg` degrees about the x axis
    pub fn rotate_x(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([1f32, 0f32, 0f32, 0f32,
                              0f32, c, -s, 0f32,
                              0f32, s, c, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate `deg` degrees about the y axis
    pub fn rotate_y(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([c, 0f32, s, 0f32,
                              0f32, 1f32, 0f32, 0f32,
                              -s, 0f32, c, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate `deg` degrees about the z axis
    pub fn rotate_z(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([c, -s, 0f32, 0f32,
                              s, c, 0f32, 0f32,
                              0f32, 0f32, 1f32, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate about `axis` by `deg` degrees
    pub fn rotate(axis: &Vector, deg: f32) -> Transform {
        let a = axis.normalized();
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let mut m = Matrix4::identity();
        *m.at_mut(0, 0) = a.x * a.x + (1f32 - a.x * a.x) * c;
        *m.at_mut(0, 1) = a.x * a.y * (1f32 - c) - a.z * s;
        *m.at_mut(0, 2) = a.x * a.z * (1f32 - c) + a.y * s;

        *m.at_mut(1, 0) = a.x * a.y * (1f32 - c) + a.z * s;
        *m.at_mut(1, 1) = a.y * a.y + (1f32 - a.y * a.y) * c;
        *m.at_mut(1, 2) = a.y * a.z * (1f32 - c) - a.x * s;

        *m.at_mut(2, 0) = a.x * a.z * (1f32 - c) - a.y * s;
        *m.at_mut(2, 1) = a.y * a.z * (1f32 - c) + a.x * s;
        *m.at_mut(2, 2) = a.z * a.z + (1f32 - a.z * a.z) * c;
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct the look at transform for a camera at `pos` looking at
    /// the point `center` oriented with up vector `up`
    pub fn look_at(pos: &Point, center: &Point, up: &Vector) -> Transform {
        let dir = (*center - *pos).normalized();
        let right = linalg::cross(&dir, up).normalized();
        let u = linalg::cross(&dir, &right).normalized();
        let mut m = Matrix4::identity();
        for i in range(0u, 3u) {
            *m.at_mut(i, 0) = right[i];
            *m.at_mut(i, 1) = u[i];
            *m.at_mut(i, 2) = dir[i];
            *m.at_mut(i, 3) = pos[i];
        }
        Transform { mat: m, inv: m.inverse() }
    }
    /// Construct a perspective transformation
    pub fn perspective(fovy: f32, near: f32, far: f32) -> Transform {
        let proj_div = Matrix4::new([1f32, 0f32, 0f32, 0f32,
                                     0f32, 1f32, 0f32, 0f32,
                                     0f32, 0f32, far / (far - near), -far * near / (far - near),
                                     0f32, 0f32, 1f32, 0f32]);
        let inv_tan = 1f32 / FloatMath::atan(linalg::radians(fovy) / 2f32);
        Transform::scale(&Vector::new(inv_tan, inv_tan, 1f32)) * Transform::from_mat(&proj_div)
    }
    /// Return the inverse of the transformation
    pub fn inverse(&self) -> Transform {
        Transform { mat: self.inv, inv: self.mat }
    }
    /// Apply the transformation to a point
    pub fn point(&self, p: &Point) -> Point {
        let mut res = Point::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.mat.at(i, 0) * p.x + *self.mat.at(i, 1) * p.y
                + *self.mat.at(i, 2) * p.z + *self.mat.at(i, 3);
        }
        let w = *self.mat.at(3, 0) * p.x + *self.mat.at(3, 1) * p.y
            + *self.mat.at(3, 2) * p.z + *self.mat.at(3, 3);
        if w != 1f32 {
            res / w
        } else {
            res
        }
    }
    /// Apply the transformation to a vector
    pub fn vector(&self, v: &Vector) -> Vector {
        let mut res = Vector::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.mat.at(i, 0) * v.x + *self.mat.at(i, 1) * v.y
                + *self.mat.at(i, 2) * v.z;
        }
        res
    }
    /// Apply the transformation to a normal
    pub fn normal(&self, n: &Normal) -> Normal {
        let mut res = Normal::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.inv.at(0, i) * n.x + *self.inv.at(1, i) * n.y
                + *self.inv.at(2, i) * n.z;
        }
        res
    }
    /// Apply the transformation to a ray
    pub fn ray(&self, ray: &Ray) -> Ray {
        let mut res = *ray;
        res.o = self.point(&res.o);
        res.d = self.vector(&res.d);
        res
    }
}

impl Mul<Transform, Transform> for Transform {
    /// Compose two transformations
    fn mul(self, rhs: Transform) -> Transform {
        Transform { mat: self.mat * rhs.mat, inv: rhs.inv * self.inv }
    }
}

<commit_msg>Should be using 1 / tan not 1 / atan<commit_after>use std::num::FloatMath;
use linalg;
use linalg::Matrix4;
use linalg::Vector;
use linalg::Point;
use linalg::Normal;
use linalg::Ray;

/// Transform describes an affine transformation in 3D space
/// and stores both the transformation and its inverse
#[deriving(Show, Copy, PartialEq)]
pub struct Transform {
    pub mat: Matrix4,
    pub inv: Matrix4,
}

impl Transform {
    /// Construct the identity transformation
    pub fn identity() -> Transform {
        Transform {
            mat: Matrix4::identity(),
            inv: Matrix4::identity(),
        }
    }
    /// Construct a transform from an existing matrix
    pub fn from_mat(mat: &Matrix4) -> Transform {
        Transform { mat: *mat, inv: mat.inverse() }
    }
    /// Construct a transform from an existing matrix/inverse pair
    pub fn from_pair(mat: &Matrix4, inv: &Matrix4) -> Transform {
        Transform { mat: *mat, inv: *inv }
    }
    /// Construct a transformation matrix to translate by the vector
    pub fn translate(v: &Vector) -> Transform {
        Transform {
            mat: Matrix4::new([1f32, 0f32, 0f32, v.x,
                               0f32, 1f32, 0f32, v.y,
                               0f32, 0f32, 1f32, v.z,
                               0f32, 0f32, 0f32, 1f32]),
            inv: Matrix4::new([1f32, 0f32, 0f32, -v.x,
                               0f32, 1f32, 0f32, -v.y,
                               0f32, 0f32, 1f32, -v.z,
                               0f32, 0f32, 0f32, 1f32]),
        }
    }
    /// Construct a transform to scale x, y and z by the values in the vector
    pub fn scale(v: &Vector) -> Transform {
        Transform {
            mat: Matrix4::new([v.x, 0f32, 0f32, 0f32,
                               0f32, v.y, 0f32, 0f32,
                               0f32, 0f32, v.z, 0f32,
                               0f32, 0f32, 0f32, 1f32]),
            inv: Matrix4::new([1f32 / v.x, 0f32, 0f32, 0f32,
                               0f32, 1f32 / v.y, 0f32, 0f32,
                               0f32, 0f32, 1f32 / v.z, 0f32,
                               0f32, 0f32, 0f32, 1f32]),
        }
    }
    /// Construct a transform to rotate `deg` degrees about the x axis
    pub fn rotate_x(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([1f32, 0f32, 0f32, 0f32,
                              0f32, c, -s, 0f32,
                              0f32, s, c, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate `deg` degrees about the y axis
    pub fn rotate_y(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([c, 0f32, s, 0f32,
                              0f32, 1f32, 0f32, 0f32,
                              -s, 0f32, c, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate `deg` degrees about the z axis
    pub fn rotate_z(deg: f32) -> Transform {
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let m = Matrix4::new([c, -s, 0f32, 0f32,
                              s, c, 0f32, 0f32,
                              0f32, 0f32, 1f32, 0f32,
                              0f32, 0f32, 0f32, 1f32]);
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct a transform to rotate about `axis` by `deg` degrees
    pub fn rotate(axis: &Vector, deg: f32) -> Transform {
        let a = axis.normalized();
        let r = linalg::radians(deg);
        let s = FloatMath::sin(r);
        let c = FloatMath::cos(r);
        let mut m = Matrix4::identity();
        *m.at_mut(0, 0) = a.x * a.x + (1f32 - a.x * a.x) * c;
        *m.at_mut(0, 1) = a.x * a.y * (1f32 - c) - a.z * s;
        *m.at_mut(0, 2) = a.x * a.z * (1f32 - c) + a.y * s;

        *m.at_mut(1, 0) = a.x * a.y * (1f32 - c) + a.z * s;
        *m.at_mut(1, 1) = a.y * a.y + (1f32 - a.y * a.y) * c;
        *m.at_mut(1, 2) = a.y * a.z * (1f32 - c) - a.x * s;

        *m.at_mut(2, 0) = a.x * a.z * (1f32 - c) - a.y * s;
        *m.at_mut(2, 1) = a.y * a.z * (1f32 - c) + a.x * s;
        *m.at_mut(2, 2) = a.z * a.z + (1f32 - a.z * a.z) * c;
        Transform { mat: m, inv: m.transpose() }
    }
    /// Construct the look at transform for a camera at `pos` looking at
    /// the point `center` oriented with up vector `up`
    pub fn look_at(pos: &Point, center: &Point, up: &Vector) -> Transform {
        let dir = (*center - *pos).normalized();
        let right = linalg::cross(&dir, up).normalized();
        let u = linalg::cross(&dir, &right).normalized();
        let mut m = Matrix4::identity();
        for i in range(0u, 3u) {
            *m.at_mut(i, 0) = right[i];
            *m.at_mut(i, 1) = u[i];
            *m.at_mut(i, 2) = dir[i];
            *m.at_mut(i, 3) = pos[i];
        }
        Transform { mat: m, inv: m.inverse() }
    }
    /// Construct a perspective transformation
    pub fn perspective(fovy: f32, near: f32, far: f32) -> Transform {
        let proj_div = Matrix4::new([1f32, 0f32, 0f32, 0f32,
                                     0f32, 1f32, 0f32, 0f32,
                                     0f32, 0f32, far / (far - near), -far * near / (far - near),
                                     0f32, 0f32, 1f32, 0f32]);
        let inv_tan = 1f32 / FloatMath::tan(linalg::radians(fovy) / 2f32);
        Transform::scale(&Vector::new(inv_tan, inv_tan, 1f32)) * Transform::from_mat(&proj_div)
    }
    /// Return the inverse of the transformation
    pub fn inverse(&self) -> Transform {
        Transform { mat: self.inv, inv: self.mat }
    }
    /// Apply the transformation to a point
    pub fn point(&self, p: &Point) -> Point {
        let mut res = Point::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.mat.at(i, 0) * p.x + *self.mat.at(i, 1) * p.y
                + *self.mat.at(i, 2) * p.z + *self.mat.at(i, 3);
        }
        let w = *self.mat.at(3, 0) * p.x + *self.mat.at(3, 1) * p.y
            + *self.mat.at(3, 2) * p.z + *self.mat.at(3, 3);
        if w != 1f32 {
            res / w
        } else {
            res
        }
    }
    /// Apply the transformation to a vector
    pub fn vector(&self, v: &Vector) -> Vector {
        let mut res = Vector::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.mat.at(i, 0) * v.x + *self.mat.at(i, 1) * v.y
                + *self.mat.at(i, 2) * v.z;
        }
        res
    }
    /// Apply the transformation to a normal
    pub fn normal(&self, n: &Normal) -> Normal {
        let mut res = Normal::broadcast(0f32);
        for i in range(0u, 3u) {
            res[i] = *self.inv.at(0, i) * n.x + *self.inv.at(1, i) * n.y
                + *self.inv.at(2, i) * n.z;
        }
        res
    }
    /// Apply the transformation to a ray
    pub fn ray(&self, ray: &Ray) -> Ray {
        let mut res = *ray;
        res.o = self.point(&res.o);
        res.d = self.vector(&res.d);
        res
    }
}

impl Mul<Transform, Transform> for Transform {
    /// Compose two transformations
    fn mul(self, rhs: Transform) -> Transform {
        Transform { mat: self.mat * rhs.mat, inv: rhs.inv * self.inv }
    }
}

<|endoftext|>


<commit_before>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};
use std::ffi::CString;
use std::ops::Deref;
use std::ptr;

pub type Display = *const c_void;
pub type Window = c_int;

pub type KeySym = *const c_void;
pub type KeyCode = c_uint;

type Bool = c_int;

#[link(name = "X11")]
extern "C" {
    fn XOpenDisplay(string: *const c_char) -> Display;
    fn XRootWindow(display: Display, index: c_int) -> Window;
    fn XFree(data: *const c_void) -> c_int;
    fn XFlush(display: Display) -> c_int;

    fn XStringToKeysym(string: *const c_char) -> KeySym;
    fn XKeysymToKeycode(display: Display, keysym: KeySym, index: c_int) -> KeyCode;
    fn XChangeKeyboardMapping(
        display: Display,
        first_keycode: c_int,
        keycode_count: c_int,
        keysyms: *const KeySym,
        keysyms_per_keycode_return: c_int,
    ) -> KeySym;
    fn XGetKeyboardMapping(
        display: Display,
        first_keycode: KeyCode,
        keycode_count: c_int,
        keysyms_per_keycode_return: *mut c_int,
    ) -> *mut KeySym;
    fn XDisplayKeycodes(
        display: Display,
        min_keycodes_return: *mut c_int,
        max_keycodes_return: *mut c_int,
    ) -> c_int;

    fn XWarpPointer(
        display: Display,
        src_w: Window,
        dest_w: Window,
        src_x: c_int,
        src_y: c_int,
        src_width: c_int,
        src_height: c_int,
        dest_x: c_int,
        dest_y: c_int,
    );
}

#[link(name = "Xtst")]
extern "C" {
    fn XTestFakeKeyEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
    fn XTestFakeButtonEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
}

/// The main struct for handling the event emitting
pub struct Enigo {
    display: Display,
    window: Window,
}

impl Enigo {
    /// Constructs a new `Enigo` instance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use enigo::*;
    /// let mut enigo = Enigo::new();
    /// ```
    pub fn new() -> Self {
        let display;
        unsafe { display = XOpenDisplay(ptr::null()) };
        if display.is_null() {
            panic!("can't open display");
        }

        let window = unsafe { XRootWindow(display, 0) };

        Enigo {
            display: display,
            window: window,
        }
    }

    // TODO(dustin): implement drop
}

impl Default for Enigo {
    fn default() -> Self {
        Self::new()
    }
}

impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, self.window, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_move_relative(&mut self, x: i32, y: i32) {

        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, 0, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_down(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                1,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_up(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                0,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_click(&mut self, button: MouseButton) {
        use std::{thread, time};

        self.mouse_down(button);
        thread::sleep(time::Duration::from_millis(100));
        self.mouse_up(button);
    }

    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }

    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }
}

impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        for c in sequence.chars() {
            let rust_unicode: String = format!("U{:x}", c as u32);
            let keycode = self.unicode_string_to_keycode(&rust_unicode);

            self.keycode_click(*keycode);
        }
    }

    fn key_click(&mut self, key: Key) {
        self.keycode_click(self.key_to_keycode(key));
    }

    fn key_down(&mut self, key: Key) {
        self.keycode_down(self.key_to_keycode(key));
    }

    fn key_up(&mut self, key: Key) {
        self.keycode_up(self.key_to_keycode(key));
    }
}

struct KeyGuard<'a>(&'a Enigo, u32);

impl<'a> Deref for KeyGuard<'a> {
	type Target = u32;

	fn deref(&self) -> &Self::Target {
		return &self.1;
	}
}
impl<'a> Drop for KeyGuard<'a> {
	fn drop(&mut self) {
		self.0.reset_keycode(self.1);
	}
}

impl Enigo {
    fn reset_keycode(&self, keycode: u32) {
        unsafe {
            let keysym_list = [0 as KeySym, 0 as KeySym];
            let keysym_list = keysym_list.as_ptr();
            XChangeKeyboardMapping(self.display, keycode as i32, 2, keysym_list, 1);
        }
    }

    fn unicode_string_to_keycode(&self, unicode_string: &str) -> KeyGuard {
        let mut keysyms_per_keycode = 0;
        // scratch space for temporary keycode bindings
        let mut scratch_keycode = 0;
        let mut keycode_low = 0;
        let mut keycode_high = 0;

        let keysyms = unsafe {
            // get the range of keycodes usually from 8 - 255
            XDisplayKeycodes(self.display, &mut keycode_low, &mut keycode_high);
            // get all the mapped keysysms available
            let keycode_count = keycode_high - keycode_low;
            XGetKeyboardMapping(
                self.display,
                keycode_low as u32,
                keycode_count,
                &mut keysyms_per_keycode,
            )
        };

        // find unused keycode for unmapped keysyms so we can
        // hook up our own keycode and map every keysym on it
        // so we just need to 'click' our once unmapped keycode
        for cidx in keycode_low..keycode_high + 1 {
            let mut key_is_empty = true;
            for sidx in 0..keysyms_per_keycode {
                let map_idx = (cidx - keycode_low) * keysyms_per_keycode + sidx;
                let sym_at_idx = unsafe { keysyms.offset(map_idx as isize) };
                if unsafe { *sym_at_idx } != 0 as *const c_void {
                    key_is_empty = false;
                } else {
                    break;
                }
            }
            if key_is_empty {
                scratch_keycode = cidx;
                break;
            }
        }

        unsafe {
            XFree(keysyms as *mut c_void);
            XFlush(self.display);
        }

        // TODO(dustin) make this an error!
        if scratch_keycode == 0 {
            panic!("cannot find free keycode");
        }

        // find the keysym for the given unicode char
        // map that keysym to our previous unmapped keycode
        // click that keycode/'button' with our keysym on it
        let unicode_as_c_string = CString::new(unicode_string).unwrap();
        let keysym = unsafe { XStringToKeysym(unicode_as_c_string.as_ptr() as *mut c_char) };
        let keysym_list = [keysym, keysym];
        let keysym_list = keysym_list.as_ptr();
        unsafe {
            XChangeKeyboardMapping(self.display, scratch_keycode, 2, keysym_list, 1);
            XFlush(self.display);
        }

        KeyGuard(self, scratch_keycode as u32)
    }

    fn key_to_keycode(&self, key: Key) -> u32 {
        unsafe {
            match key {
                Key::Return => XKeysymToKeycode(self.display, XK_RETURN as *const c_void, 0),
                Key::Tab => XKeysymToKeycode(self.display, XK_TAB as *const c_void, 0),
                Key::Space => XKeysymToKeycode(self.display, XK_SPACE as *const c_void, 0),
                Key::Backspace => XKeysymToKeycode(self.display, XK_BACKSPACE as *const c_void, 0),
                Key::Escape => XKeysymToKeycode(self.display, XK_ESCAPE as *const c_void, 0),
                Key::Super => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Command => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Windows => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Shift => XKeysymToKeycode(self.display, XK_SHIFT_L as *const c_void, 0),
                Key::CapsLock => XKeysymToKeycode(self.display, XK_CAPS_LOCK as *const c_void, 0),
                Key::Alt => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Option => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Control => XKeysymToKeycode(self.display, XK_CONTROL_L as *const c_void, 0),
                Key::Home => XKeysymToKeycode(self.display, XK_HOME as *const c_void, 0),
                Key::PageUp => XKeysymToKeycode(self.display, XK_PAGE_UP as *const c_void, 0),
                Key::PageDown => XKeysymToKeycode(self.display, XK_PAGE_DOWN as *const c_void, 0),
                Key::LeftArrow => XKeysymToKeycode(self.display, XK_LEFT as *const c_void, 0),
                Key::RightArrow => XKeysymToKeycode(self.display, XK_RIGHT as *const c_void, 0),
                Key::DownArrow => XKeysymToKeycode(self.display, XK_DOWN as *const c_void, 0),
                Key::UpArrow => XKeysymToKeycode(self.display, XK_UP as *const c_void, 0),
                Key::F1 => XKeysymToKeycode(self.display, XK_F1 as *const c_void, 0),
                Key::F2 => XKeysymToKeycode(self.display, XK_F2 as *const c_void, 0),
                Key::F3 => XKeysymToKeycode(self.display, XK_F3 as *const c_void, 0),
                Key::F4 => XKeysymToKeycode(self.display, XK_F4 as *const c_void, 0),
                Key::F5 => XKeysymToKeycode(self.display, XK_F5 as *const c_void, 0),
                Key::F6 => XKeysymToKeycode(self.display, XK_F6 as *const c_void, 0),
                Key::F7 => XKeysymToKeycode(self.display, XK_F7 as *const c_void, 0),
                Key::F8 => XKeysymToKeycode(self.display, XK_F8 as *const c_void, 0),
                Key::F9 => XKeysymToKeycode(self.display, XK_F9 as *const c_void, 0),
                Key::F10 => XKeysymToKeycode(self.display, XK_F10 as *const c_void, 0),
                Key::F11 => XKeysymToKeycode(self.display, XK_F11 as *const c_void, 0),
                Key::F12 => XKeysymToKeycode(self.display, XK_F12 as *const c_void, 0),

                Key::Raw(raw_keycode) => raw_keycode as u32,
                Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()),
            }
        }
    }

    fn get_layoutdependent_keycode(&self, string: String) -> u32 {
        let c_string = CString::new(string).unwrap();
        let keysym = unsafe { XStringToKeysym(c_string.as_ptr() as *mut c_char) };

        unsafe { XKeysymToKeycode(self.display, keysym, 0) }
    }

    fn keycode_click(&self, keycode: u32) {
        self.keycode_down(keycode);
        self.keycode_up(keycode);
    }

    fn keycode_down(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 1, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }

    fn keycode_up(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 0, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }
}
<commit_msg>Rewrite in libxdo<commit_after>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};
use std::borrow::Cow;
use std::ffi::CString;
use std::ops::Deref;
use std::ptr;

const CURRENT_WINDOW: c_int = 0;
type Window  = c_int;
type Xdo     = *const c_void;

#[link(name = "xdo")]
extern "C" {
    fn xdo_free(xdo: Xdo);
    fn xdo_new(display: *const c_char) -> Xdo;

    fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
    fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;

    fn xdo_enter_text_window(xdo: Xdo, window: Window, string: *const c_char, delay: c_ulong);
    fn xdo_send_keysequence_window(xdo: Xdo, window: Window, string: *const c_char, delay: c_ulong);
    fn xdo_send_keysequence_window_down(xdo: Xdo, window: Window, string: *const c_char, delay: c_ulong);
    fn xdo_send_keysequence_window_up(xdo: Xdo, window: Window, string: *const c_char, delay: c_ulong);
}

fn mousebutton(button: MouseButton) -> i32 {
    match button {
        MouseButton::Left => 1,
        MouseButton::Middle => 2,
        MouseButton::Right => 3,
        MouseButton::ScrollUp => 4,
        MouseButton::ScrollDown => 5,
        MouseButton::ScrollLeft => 6,
        MouseButton::ScrollRight => 7
    }
}

/// The main struct for handling the event emitting
pub struct Enigo {
    xdo: Xdo
}
impl Enigo {
    /// Create a new Enigo instance
    pub fn new() -> Enigo {
        Enigo {
            xdo: unsafe { xdo_new(ptr::null()) }
        }
    }
}
impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse(self.xdo, x, y, 0);
        }
    }
    fn mouse_move_relative(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse_relative(self.xdo, x, y);
        }
    }
    fn mouse_down(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_up(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_click(&mut self, button: MouseButton) {
        unsafe {
            xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
}
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
    if let Key::Layout(c) = key {
        return Cow::from(c.to_string());
    }
    Cow::from(match key {
        Key::Alt => "Alt",
        Key::Backspace => "Backspace",
        Key::CapsLock => "CapsLock",
        Key::Command => "Command",
        Key::Control => "Control",
        Key::DownArrow => "DownArrow",
        Key::Escape => "Escape",
        Key::F1 => "F1",
        Key::F2 => "F2",
        Key::F3 => "F3",
        Key::F4 => "F4",
        Key::F5 => "F5",
        Key::F6 => "F6",
        Key::F7 => "F7",
        Key::F8 => "F8",
        Key::F9 => "F9",
        Key::F10 => "F10",
        Key::F11 => "F11",
        Key::F12 => "F12",
        Key::Home => "Home",
        Key::Layout(_) => unreachable!(),
        Key::LeftArrow => "Left",
        Key::Option => "Option",
        Key::PageDown => "PageDown",
        Key::PageUp => "PageUp",
        Key::Raw(_) => unimplemented!(),
        Key::Return => "Return",
        Key::RightArrow => "Right",
        Key::Shift => "Shift",
        Key::Space => "Space",
        Key::Super => "Super",
        Key::Tab => "Tab",
        Key::UpArrow => "Up",
        Key::Windows => "Windows"
    })
}
impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        let string = CString::new(sequence).unwrap();
        unsafe {
            xdo_enter_text_window(self.xdo, CURRENT_WINDOW, string.as_ptr(), 12000);
        }
    }
    fn key_down(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_down(self.xdo, CURRENT_WINDOW, string.as_ptr(), 12000);
        }
    }
    fn key_up(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_up(self.xdo, CURRENT_WINDOW, string.as_ptr(), 12000);
        }
    }
    fn key_click(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window(self.xdo, CURRENT_WINDOW, string.as_ptr(), 12000);
        }
    }
}
<|endoftext|>


<commit_before>//! A convenience struct to create symbolic trees.

use ketos::{
    Error,
    Interpreter,
    ModuleLoader,
    FromValue,
};
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::BufReader;
use std::fmt::Debug;
use std::fs::File;
use std::rc::Rc;
use std::cell::RefCell;
use tree::{
    Tree,
    Expression,
    TreeError,
    TreeErrorKind,
};

/// Contains the code to be executed as a prologue, body, and epilogue.
pub struct TreeBuilder {
    kcode: String,
    prologue: String,
    epilogue: String,
}

impl TreeBuilder {
    /// Creates a new TreeBuilder with provided 'body' code.
    pub fn new(body: &str) -> TreeBuilder {
        TreeBuilder {
            kcode: body.to_string(),
            prologue: "".to_string(),
            epilogue: "".to_string(),
        }
    }

    /// Creates a new TreeBuilder from a file.
    pub fn from_file(path: &str) -> Result<TreeBuilder, ::std::io::Error> {
        let kf = File::open(path)?;
        let mut reader = BufReader::new(kf);
        let mut kfiledata = String::new();

        reader.read_to_string(&mut kfiledata)?;

        Ok(TreeBuilder {
            kcode: kfiledata,
            prologue: "".to_string(),
            epilogue: "".to_string(),
        })
    }

    /// Sets the prologue to be executed.
    pub fn set_prologue(&mut self, s: &str) {
        self.prologue = s.to_string();
    }

    /// Sets the epilogue to be executed.
    pub fn set_epilogue(&mut self, s: &str) {
        self.epilogue = s.to_string();
    }

    /// Takes a Box containing a ketos::ModuleLoader and a name to look for in the file that is
    ///  assigned to the tree using define in the body of code.
    ///
    /// Returns a tuple containing the Tree and a HashSet of variable names.
    pub fn use_box_and_name<T: 'static + Clone + Debug, B: 'static + ModuleLoader>(&self, cml: Box<B>, tree_name: &str) -> Result<(Tree<T>, HashSet<String>), Error> {
        let result: (Tree<T>, HashSet<String>);
        {
            let interp = Interpreter::with_loader(cml);

            let varcont = Rc::new(RefCell::new(HashSet::<String>::new()));
            let varcontc = varcont.clone();

            let var_fn = move |s: &str| -> Result<Tree<T>, Error> {
                varcontc.borrow_mut().insert(s.to_string());
                Ok(Tree::<T>::new(Expression::Variable(s.to_string())))
            };

            // It turns out that this macro just uses the Fn trait and clever syntax.
            ketos_fn!{ interp.scope() => "var" => fn var_fn(s: &str) -> Tree<T> }

            interp.run_code(&format!("{}\n{}\n{}", self.prologue, self.kcode, self.epilogue), None)?;

            let m = Tree::from_value(match interp.get_value(tree_name) {
                Some(n) => n,
                None => { return Err(Error::custom(TreeError::create(TreeErrorKind::TreeNotInScope))); },
            })?;

            result = (m, varcont.borrow().clone());
        }
        Ok(result)
    }
}

<commit_msg>Clean up some documentation<commit_after>//! A convenience struct to create symbolic trees.

use ketos::{
    Error,
    Interpreter,
    ModuleLoader,
    FromValue,
};
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::BufReader;
use std::fmt::Debug;
use std::fs::File;
use std::rc::Rc;
use std::cell::RefCell;
use tree::{
    Tree,
    Expression,
    TreeError,
    TreeErrorKind,
};

/// Contains the code to be executed as a prologue, body, and epilogue.
pub struct TreeBuilder {
    kcode: String,
    prologue: String,
    epilogue: String,
}

impl TreeBuilder {
    /// Creates a new TreeBuilder with provided 'body' code.
    pub fn new(body: &str) -> TreeBuilder {
        TreeBuilder {
            kcode: body.to_string(),
            prologue: "".to_string(),
            epilogue: "".to_string(),
        }
    }

    /// Creates a new TreeBuilder from a file.
    pub fn from_file(path: &str) -> Result<TreeBuilder, ::std::io::Error> {
        let kf = File::open(path)?;
        let mut reader = BufReader::new(kf);
        let mut kfiledata = String::new();

        reader.read_to_string(&mut kfiledata)?;

        Ok(TreeBuilder {
            kcode: kfiledata,
            prologue: "".to_string(),
            epilogue: "".to_string(),
        })
    }

    /// Sets the prologue to be executed.
    pub fn set_prologue(&mut self, s: &str) {
        self.prologue = s.to_string();
    }

    /// Sets the epilogue to be executed.
    pub fn set_epilogue(&mut self, s: &str) {
        self.epilogue = s.to_string();
    }

    /// Takes a Box containing a ketos::ModuleLoader and a name to look for that the tree
    ///  is assigned to using define in the body of Ketos code.
    ///
    /// Returns a tuple containing the Tree and a HashSet of variable names.
    pub fn use_box_and_name<T: 'static + Clone + Debug, B: 'static + ModuleLoader>(&self, cml: Box<B>, tree_name: &str) -> Result<(Tree<T>, HashSet<String>), Error> {
        let result: (Tree<T>, HashSet<String>);
        {
            let interp = Interpreter::with_loader(cml);

            let varcont = Rc::new(RefCell::new(HashSet::<String>::new()));
            let varcontc = varcont.clone();

            let var_fn = move |s: &str| -> Result<Tree<T>, Error> {
                varcontc.borrow_mut().insert(s.to_string());
                Ok(Tree::<T>::new(Expression::Variable(s.to_string())))
            };

            // It turns out that this macro just uses the Fn trait and clever syntax.
            ketos_fn!{ interp.scope() => "var" => fn var_fn(s: &str) -> Tree<T> }

            interp.run_code(&format!("{}\n{}\n{}", self.prologue, self.kcode, self.epilogue), None)?;

            let m = Tree::from_value(match interp.get_value(tree_name) {
                Some(n) => n,
                None => { return Err(Error::custom(TreeError::create(TreeErrorKind::TreeNotInScope))); },
            })?;

            result = (m, varcont.borrow().clone());
        }
        Ok(result)
    }
}

<|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/.

use std::{
    collections::{hash_map::RandomState, HashSet},
    fs::File,
    io::{self, Read},
    iter::FromIterator,
    os::unix::io::{FromRawFd, RawFd},
    path::{Path, PathBuf},
};

use termios::Termios;

use devicemapper::Bytes;
use libcryptsetup_rs::SafeMemHandle;

use crate::{
    engine::{
        engine::{Pool, MAX_STRATIS_PASS_SIZE},
        types::{BlockDevTier, CreateAction, DevUuid, PoolUuid, SetCreateAction, SizedKeyMemory},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn create_pool_idempotent_or_err(
    pool: &dyn Pool,
    pool_name: &str,
    blockdev_paths: &[&Path],
) -> StratisResult<CreateAction<PoolUuid>> {
    let input_devices: HashSet<PathBuf, RandomState> =
        blockdev_paths.iter().map(|p| p.to_path_buf()).collect();

    let existing_paths: HashSet<PathBuf, _> = pool
        .blockdevs()
        .iter()
        .filter_map(|(_, tier, bd)| {
            if *tier == BlockDevTier::Data {
                Some(bd.devnode().physical_path().to_owned())
            } else {
                None
            }
        })
        .collect();

    if input_devices == existing_paths {
        Ok(CreateAction::Identity)
    } else {
        let in_input = input_devices
            .difference(&existing_paths)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_paths
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            create_pool_generate_error_string!(pool_name, in_input, in_pool),
        ))
    }
}

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn init_cache_idempotent_or_err<I>(
    blockdev_paths: &[&Path],
    existing_iter: I,
) -> StratisResult<SetCreateAction<DevUuid>>
where
    I: Iterator<Item = PathBuf>,
{
    let input_devices = HashSet::from_iter(blockdev_paths.iter().map(|p| p.to_path_buf()));
    let existing_devices = HashSet::<_, RandomState>::from_iter(existing_iter);
    if input_devices == existing_devices {
        Ok(SetCreateAction::empty())
    } else {
        let in_input = input_devices
            .difference(&existing_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_devices
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            init_cache_generate_error_string!(in_input, in_pool),
        ))
    }
}

/// Shared implementation of setting keys in the keyring for both the strat_engine
/// and sim_engine.
pub fn set_key_shared(key_fd: RawFd, interactive: Option<bool>) -> StratisResult<SizedKeyMemory> {
    fn read_loop(
        bytes_iter: &mut io::Bytes<File>,
        mem: &mut [u8],
        interactive: bool,
    ) -> StratisResult<usize> {
        let mut pos = 0;
        while pos < MAX_STRATIS_PASS_SIZE {
            match bytes_iter.next() {
                Some(Ok(b)) => {
                    if interactive && b as char == '\n' {
                        break;
                    }

                    mem[pos] = b;
                    pos += 1;
                }
                Some(Err(e)) => return Err(e.into()),
                None => break,
            }
        }
        Ok(pos)
    }

    let key_file = unsafe { File::from_raw_fd(key_fd) };
    let mut memory = SafeMemHandle::alloc(MAX_STRATIS_PASS_SIZE)?;

    let old_attrs = if let Some(true) = interactive {
        let old_attrs = Termios::from_fd(key_fd)?;
        let mut new_attrs = old_attrs;
        new_attrs.c_lflag &= !(termios::ICANON | termios::ECHO);
        new_attrs.c_cc[termios::VMIN] = 1;
        new_attrs.c_cc[termios::VTIME] = 0;
        termios::tcsetattr(key_fd, termios::TCSANOW, &new_attrs)?;
        Some(old_attrs)
    } else {
        None
    };

    let mut bytes_iter = key_file.bytes();

    let res = read_loop(&mut bytes_iter, memory.as_mut(), interactive.is_some());

    if let Some(ref oa) = old_attrs {
        termios::tcsetattr(key_fd, termios::TCSANOW, oa)?;
    }

    let pos = res?;

    if pos == MAX_STRATIS_PASS_SIZE && bytes_iter.next().is_some() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!(
                "Provided key exceeded maximum allow length of {}",
                Bytes(MAX_STRATIS_PASS_SIZE as u64)
            ),
        ));
    }

    let sized_memory = SizedKeyMemory::new(memory, pos);

    Ok(sized_memory)
}

/// Validate a str for use as a Pool or Filesystem name.
pub fn validate_name(name: &str) -> StratisResult<()> {
    let name_path = Path::new(name);
    if name.contains('\u{0}') {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains NULL characters : {}", name),
        ));
    }
    if name_path.components().count() != 1 {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is a path with 0 or more than 1 components : {}", name),
        ));
    }
    if name_path.is_absolute() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is an absolute path : {}", name),
        ));
    }
    if name == "." || name == ".." {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is . or .. : {}", name),
        ));
    }
    // Linux has a maximum filename length of 255 bytes
    if name.len() > 255 {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name has more than 255 bytes : {}", name),
        ));
    }

    if name.len() != name.trim().len() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains leading or trailing space : {}", name),
        ));
    }
    if name.chars().any(|c| c.is_control()) {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains control characters : {}", name),
        ));
    }
    Ok(())
}

/// Verify that all paths are absolute.
pub fn validate_paths(paths: &[&Path]) -> StratisResult<()> {
    let non_absolute_paths: Vec<&Path> = paths
        .iter()
        .filter(|path| !path.is_absolute())
        .cloned()
        .collect();
    if non_absolute_paths.is_empty() {
        Ok(())
    } else {
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!(
                "Paths{{{}}} are not absolute",
                non_absolute_paths
                    .iter()
                    .map(|p| p.display().to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
        ))
    }
}

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

    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn test_validate_name() {
        assert_matches!(validate_name(&'\u{0}'.to_string()), Err(_));
        assert_matches!(validate_name("./some"), Err(_));
        assert_matches!(validate_name("../../root"), Err(_));
        assert_matches!(validate_name("/"), Err(_));
        assert_matches!(validate_name("\u{1c}\u{7}"), Err(_));
        assert_matches!(validate_name("./foo/bar.txt"), Err(_));
        assert_matches!(validate_name("."), Err(_));
        assert_matches!(validate_name(".."), Err(_));
        assert_matches!(validate_name("/dev/sdb"), Err(_));
        assert_matches!(validate_name(""), Err(_));
        assert_matches!(validate_name("/"), Err(_));
        assert_matches!(validate_name(" leading_space"), Err(_));
        assert_matches!(validate_name("trailing_space "), Err(_));
        assert_matches!(validate_name("\u{0}leading_null"), Err(_));
        assert_matches!(validate_name("trailing_null\u{0}"), Err(_));
        assert_matches!(validate_name("middle\u{0}_null"), Err(_));
        assert_matches!(validate_name("\u{0}multiple\u{0}_null\u{0}"), Err(_));
        assert_matches!(validate_name(&"𐌏".repeat(64)), Err(_));

        assert_matches!(validate_name(&"𐌏".repeat(63)), Ok(_));
        assert_matches!(validate_name(&'\u{10fff8}'.to_string()), Ok(_));
        assert_matches!(validate_name("*< ? >"), Ok(_));
        assert_matches!(validate_name("..."), Ok(_));
        assert_matches!(validate_name("ok.name"), Ok(_));
        assert_matches!(validate_name("ok name with spaces"), Ok(_));
        assert_matches!(validate_name("\\\\"), Ok(_));
        assert_matches!(validate_name("\u{211D}"), Ok(_));
        assert_matches!(validate_name("☺"), Ok(_));
        assert_matches!(validate_name("ok_name"), Ok(_));
    }
}
<commit_msg>Check string properties of name before path properties<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/.

use std::{
    collections::{hash_map::RandomState, HashSet},
    fs::File,
    io::{self, Read},
    iter::FromIterator,
    os::unix::io::{FromRawFd, RawFd},
    path::{Path, PathBuf},
};

use termios::Termios;

use devicemapper::Bytes;
use libcryptsetup_rs::SafeMemHandle;

use crate::{
    engine::{
        engine::{Pool, MAX_STRATIS_PASS_SIZE},
        types::{BlockDevTier, CreateAction, DevUuid, PoolUuid, SetCreateAction, SizedKeyMemory},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn create_pool_idempotent_or_err(
    pool: &dyn Pool,
    pool_name: &str,
    blockdev_paths: &[&Path],
) -> StratisResult<CreateAction<PoolUuid>> {
    let input_devices: HashSet<PathBuf, RandomState> =
        blockdev_paths.iter().map(|p| p.to_path_buf()).collect();

    let existing_paths: HashSet<PathBuf, _> = pool
        .blockdevs()
        .iter()
        .filter_map(|(_, tier, bd)| {
            if *tier == BlockDevTier::Data {
                Some(bd.devnode().physical_path().to_owned())
            } else {
                None
            }
        })
        .collect();

    if input_devices == existing_paths {
        Ok(CreateAction::Identity)
    } else {
        let in_input = input_devices
            .difference(&existing_paths)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_paths
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            create_pool_generate_error_string!(pool_name, in_input, in_pool),
        ))
    }
}

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn init_cache_idempotent_or_err<I>(
    blockdev_paths: &[&Path],
    existing_iter: I,
) -> StratisResult<SetCreateAction<DevUuid>>
where
    I: Iterator<Item = PathBuf>,
{
    let input_devices = HashSet::from_iter(blockdev_paths.iter().map(|p| p.to_path_buf()));
    let existing_devices = HashSet::<_, RandomState>::from_iter(existing_iter);
    if input_devices == existing_devices {
        Ok(SetCreateAction::empty())
    } else {
        let in_input = input_devices
            .difference(&existing_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_devices
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            init_cache_generate_error_string!(in_input, in_pool),
        ))
    }
}

/// Shared implementation of setting keys in the keyring for both the strat_engine
/// and sim_engine.
pub fn set_key_shared(key_fd: RawFd, interactive: Option<bool>) -> StratisResult<SizedKeyMemory> {
    fn read_loop(
        bytes_iter: &mut io::Bytes<File>,
        mem: &mut [u8],
        interactive: bool,
    ) -> StratisResult<usize> {
        let mut pos = 0;
        while pos < MAX_STRATIS_PASS_SIZE {
            match bytes_iter.next() {
                Some(Ok(b)) => {
                    if interactive && b as char == '\n' {
                        break;
                    }

                    mem[pos] = b;
                    pos += 1;
                }
                Some(Err(e)) => return Err(e.into()),
                None => break,
            }
        }
        Ok(pos)
    }

    let key_file = unsafe { File::from_raw_fd(key_fd) };
    let mut memory = SafeMemHandle::alloc(MAX_STRATIS_PASS_SIZE)?;

    let old_attrs = if let Some(true) = interactive {
        let old_attrs = Termios::from_fd(key_fd)?;
        let mut new_attrs = old_attrs;
        new_attrs.c_lflag &= !(termios::ICANON | termios::ECHO);
        new_attrs.c_cc[termios::VMIN] = 1;
        new_attrs.c_cc[termios::VTIME] = 0;
        termios::tcsetattr(key_fd, termios::TCSANOW, &new_attrs)?;
        Some(old_attrs)
    } else {
        None
    };

    let mut bytes_iter = key_file.bytes();

    let res = read_loop(&mut bytes_iter, memory.as_mut(), interactive.is_some());

    if let Some(ref oa) = old_attrs {
        termios::tcsetattr(key_fd, termios::TCSANOW, oa)?;
    }

    let pos = res?;

    if pos == MAX_STRATIS_PASS_SIZE && bytes_iter.next().is_some() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!(
                "Provided key exceeded maximum allow length of {}",
                Bytes(MAX_STRATIS_PASS_SIZE as u64)
            ),
        ));
    }

    let sized_memory = SizedKeyMemory::new(memory, pos);

    Ok(sized_memory)
}

/// Validate a str for use as a Pool or Filesystem name.
pub fn validate_name(name: &str) -> StratisResult<()> {
    if name.contains('\u{0}') {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains NULL characters : {}", name),
        ));
    }
    if name == "." || name == ".." {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is . or .. : {}", name),
        ));
    }
    // Linux has a maximum filename length of 255 bytes
    if name.len() > 255 {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name has more than 255 bytes : {}", name),
        ));
    }
    if name.len() != name.trim().len() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains leading or trailing space : {}", name),
        ));
    }
    if name.chars().any(|c| c.is_control()) {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name contains control characters : {}", name),
        ));
    }

    let name_path = Path::new(name);
    if name_path.components().count() != 1 {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is a path with 0 or more than 1 components : {}", name),
        ));
    }
    if name_path.is_absolute() {
        return Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!("Name is an absolute path : {}", name),
        ));
    }
    Ok(())
}

/// Verify that all paths are absolute.
pub fn validate_paths(paths: &[&Path]) -> StratisResult<()> {
    let non_absolute_paths: Vec<&Path> = paths
        .iter()
        .filter(|path| !path.is_absolute())
        .cloned()
        .collect();
    if non_absolute_paths.is_empty() {
        Ok(())
    } else {
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            format!(
                "Paths{{{}}} are not absolute",
                non_absolute_paths
                    .iter()
                    .map(|p| p.display().to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
        ))
    }
}

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

    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn test_validate_name() {
        assert_matches!(validate_name(&'\u{0}'.to_string()), Err(_));
        assert_matches!(validate_name("./some"), Err(_));
        assert_matches!(validate_name("../../root"), Err(_));
        assert_matches!(validate_name("/"), Err(_));
        assert_matches!(validate_name("\u{1c}\u{7}"), Err(_));
        assert_matches!(validate_name("./foo/bar.txt"), Err(_));
        assert_matches!(validate_name("."), Err(_));
        assert_matches!(validate_name(".."), Err(_));
        assert_matches!(validate_name("/dev/sdb"), Err(_));
        assert_matches!(validate_name(""), Err(_));
        assert_matches!(validate_name("/"), Err(_));
        assert_matches!(validate_name(" leading_space"), Err(_));
        assert_matches!(validate_name("trailing_space "), Err(_));
        assert_matches!(validate_name("\u{0}leading_null"), Err(_));
        assert_matches!(validate_name("trailing_null\u{0}"), Err(_));
        assert_matches!(validate_name("middle\u{0}_null"), Err(_));
        assert_matches!(validate_name("\u{0}multiple\u{0}_null\u{0}"), Err(_));
        assert_matches!(validate_name(&"𐌏".repeat(64)), Err(_));

        assert_matches!(validate_name(&"𐌏".repeat(63)), Ok(_));
        assert_matches!(validate_name(&'\u{10fff8}'.to_string()), Ok(_));
        assert_matches!(validate_name("*< ? >"), Ok(_));
        assert_matches!(validate_name("..."), Ok(_));
        assert_matches!(validate_name("ok.name"), Ok(_));
        assert_matches!(validate_name("ok name with spaces"), Ok(_));
        assert_matches!(validate_name("\\\\"), Ok(_));
        assert_matches!(validate_name("\u{211D}"), Ok(_));
        assert_matches!(validate_name("☺"), Ok(_));
        assert_matches!(validate_name("ok_name"), Ok(_));
    }
}
<|endoftext|>