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

//! Job management on Windows for bootstrapping
//!
//! Most of the time when you're running a build system (e.g. make) you expect
//! Ctrl-C or abnormal termination to actually terminate the entire tree of
//! process in play, not just the one at the top. This currently works "by
//! default" on Unix platforms because Ctrl-C actually sends a signal to the
//! *process group* rather than the parent process, so everything will get torn
//! down. On Windows, however, this does not happen and Ctrl-C just kills the
//! parent process.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!
//!
//! Unfortunately most of the time the build system is actually called from a
//! python wrapper (which manages things like building the build system) so this
//! all doesn't quite cut it so far. To go the last mile we duplicate the job
//! object handle into our parent process (a python process probably) and then
//! close our own handle. This means that the only handle to the job object
//! resides in the parent python process, so when python dies the whole build
//! system dies (as one would probably expect!).
//!
//! Note that this module has a #[cfg(windows)] above it as none of this logic
//! is required on Unix.

#![allow(bad_style, dead_code)]

use std::env;
use std::io;
use std::mem;

type HANDLE = *mut u8;
type BOOL = i32;
type DWORD = u32;
type LPHANDLE = *mut HANDLE;
type LPVOID = *mut u8;
type JOBOBJECTINFOCLASS = i32;
type SIZE_T = usize;
type LARGE_INTEGER = i64;
type ULONG_PTR = usize;
type ULONGLONG = u64;

const FALSE: BOOL = 0;
const DUPLICATE_SAME_ACCESS: DWORD = 0x2;
const PROCESS_DUP_HANDLE: DWORD = 0x40;
const JobObjectExtendedLimitInformation: JOBOBJECTINFOCLASS = 9;
const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: DWORD = 0x2000;

extern "system" {
    fn CreateJobObjectW(lpJobAttributes: *mut u8, lpName: *const u8) -> HANDLE;
    fn CloseHandle(hObject: HANDLE) -> BOOL;
    fn GetCurrentProcess() -> HANDLE;
    fn OpenProcess(dwDesiredAccess: DWORD,
                   bInheritHandle: BOOL,
                   dwProcessId: DWORD) -> HANDLE;
    fn DuplicateHandle(hSourceProcessHandle: HANDLE,
                       hSourceHandle: HANDLE,
                       hTargetProcessHandle: HANDLE,
                       lpTargetHandle: LPHANDLE,
                       dwDesiredAccess: DWORD,
                       bInheritHandle: BOOL,
                       dwOptions: DWORD) -> BOOL;
    fn AssignProcessToJobObject(hJob: HANDLE, hProcess: HANDLE) -> BOOL;
    fn SetInformationJobObject(hJob: HANDLE,
                               JobObjectInformationClass: JOBOBJECTINFOCLASS,
                               lpJobObjectInformation: LPVOID,
                               cbJobObjectInformationLength: DWORD) -> BOOL;
}

#[repr(C)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
    BasicLimitInformation: JOBOBJECT_BASIC_LIMIT_INFORMATION,
    IoInfo: IO_COUNTERS,
    ProcessMemoryLimit: SIZE_T,
    JobMemoryLimit: SIZE_T,
    PeakProcessMemoryUsed: SIZE_T,
    PeakJobMemoryUsed: SIZE_T,
}

#[repr(C)]
struct IO_COUNTERS {
    ReadOperationCount: ULONGLONG,
    WriteOperationCount: ULONGLONG,
    OtherOperationCount: ULONGLONG,
    ReadTransferCount: ULONGLONG,
    WriteTransferCount: ULONGLONG,
    OtherTransferCount: ULONGLONG,
}

#[repr(C)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION {
    PerProcessUserTimeLimit: LARGE_INTEGER,
    PerJobUserTimeLimit: LARGE_INTEGER,
    LimitFlags: DWORD,
    MinimumWorkingsetSize: SIZE_T,
    MaximumWorkingsetSize: SIZE_T,
    ActiveProcessLimit: DWORD,
    Affinity: ULONG_PTR,
    PriorityClass: DWORD,
    SchedulingClass: DWORD,
}

pub unsafe fn setup() {
    // Create a new job object for us to use
    let job = CreateJobObjectW(0 as *mut _, 0 as *const _);
    assert!(job != 0 as *mut _, "{}", io::Error::last_os_error());

    // Indicate that when all handles to the job object are gone that all
    // process in the object should be killed. Note that this includes our
    // entire process tree by default because we've added ourselves and our
    // children will reside in the job by default.
    let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
    info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    let r = SetInformationJobObject(job,
                                    JobObjectExtendedLimitInformation,
                                    &mut info as *mut _ as LPVOID,
                                    mem::size_of_val(&info) as DWORD);
    assert!(r != 0, "{}", io::Error::last_os_error());

    // Assign our process to this job object. Note that if this fails, one very
    // likely reason is that we are ourselves already in a job object! This can
    // happen on the build bots that we've got for Windows, or if just anyone
    // else is instrumenting the build. In this case we just bail out
    // immediately and assume that they take care of it.
    //
    // Also note that nested jobs (why this might fail) are supported in recent
    // versions of Windows, but the version of Windows that our bots are running
    // at least don't support nested job objects.
    let r = AssignProcessToJobObject(job, GetCurrentProcess());
    if r == 0 {
        CloseHandle(job);
        return
    }

    // If we've got a parent process (e.g. the python script that called us)
    // then move ownership of this job object up to them. That way if the python
    // script is killed (e.g. via ctrl-c) then we'll all be torn down.
    //
    // If we don't have a parent (e.g. this was run directly) then we
    // intentionally leak the job object handle. When our process exits
    // (normally or abnormally) it will close the handle implicitly, causing all
    // processes in the job to be cleaned up.
    let pid = match env::var("BOOTSTRAP_PARENT_ID") {
        Ok(s) => s,
        Err(..) => return,
    };

    let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
    assert!(parent != 0 as *mut _, "{}", io::Error::last_os_error());
    let mut parent_handle = 0 as *mut _;
    let r = DuplicateHandle(GetCurrentProcess(), job,
                            parent, &mut parent_handle,
                            0, FALSE, DUPLICATE_SAME_ACCESS);

    // If this failed, well at least we tried! An example of DuplicateHandle
    // failing in the past has been when the wrong python2 package spawed this
    // build system (e.g. the `python2` package in MSYS instead of
    // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
    // mode" here is that we only clean everything up when the build system
    // dies, not when the python parent does, so not too bad.
    if r != 0 {
        CloseHandle(job);
    }
}
<commit_msg>Prevent Windows from displaying UI on errors.<commit_after>// Copyright 2015 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.

//! Job management on Windows for bootstrapping
//!
//! Most of the time when you're running a build system (e.g. make) you expect
//! Ctrl-C or abnormal termination to actually terminate the entire tree of
//! process in play, not just the one at the top. This currently works "by
//! default" on Unix platforms because Ctrl-C actually sends a signal to the
//! *process group* rather than the parent process, so everything will get torn
//! down. On Windows, however, this does not happen and Ctrl-C just kills the
//! parent process.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!
//!
//! Unfortunately most of the time the build system is actually called from a
//! python wrapper (which manages things like building the build system) so this
//! all doesn't quite cut it so far. To go the last mile we duplicate the job
//! object handle into our parent process (a python process probably) and then
//! close our own handle. This means that the only handle to the job object
//! resides in the parent python process, so when python dies the whole build
//! system dies (as one would probably expect!).
//!
//! Note that this module has a #[cfg(windows)] above it as none of this logic
//! is required on Unix.

#![allow(bad_style, dead_code)]

use std::env;
use std::io;
use std::mem;

type HANDLE = *mut u8;
type BOOL = i32;
type DWORD = u32;
type LPHANDLE = *mut HANDLE;
type LPVOID = *mut u8;
type JOBOBJECTINFOCLASS = i32;
type SIZE_T = usize;
type LARGE_INTEGER = i64;
type UINT = u32;
type ULONG_PTR = usize;
type ULONGLONG = u64;

const FALSE: BOOL = 0;
const DUPLICATE_SAME_ACCESS: DWORD = 0x2;
const PROCESS_DUP_HANDLE: DWORD = 0x40;
const JobObjectExtendedLimitInformation: JOBOBJECTINFOCLASS = 9;
const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: DWORD = 0x2000;
const SEM_FAILCRITICALERRORS: UINT = 0x0001;
const SEM_NOGPFAULTERRORBOX: UINT = 0x0002;

extern "system" {
    fn CreateJobObjectW(lpJobAttributes: *mut u8, lpName: *const u8) -> HANDLE;
    fn CloseHandle(hObject: HANDLE) -> BOOL;
    fn GetCurrentProcess() -> HANDLE;
    fn OpenProcess(dwDesiredAccess: DWORD,
                   bInheritHandle: BOOL,
                   dwProcessId: DWORD) -> HANDLE;
    fn DuplicateHandle(hSourceProcessHandle: HANDLE,
                       hSourceHandle: HANDLE,
                       hTargetProcessHandle: HANDLE,
                       lpTargetHandle: LPHANDLE,
                       dwDesiredAccess: DWORD,
                       bInheritHandle: BOOL,
                       dwOptions: DWORD) -> BOOL;
    fn AssignProcessToJobObject(hJob: HANDLE, hProcess: HANDLE) -> BOOL;
    fn SetInformationJobObject(hJob: HANDLE,
                               JobObjectInformationClass: JOBOBJECTINFOCLASS,
                               lpJobObjectInformation: LPVOID,
                               cbJobObjectInformationLength: DWORD) -> BOOL;
    fn SetErrorMode(mode: UINT) -> UINT;
}

#[repr(C)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
    BasicLimitInformation: JOBOBJECT_BASIC_LIMIT_INFORMATION,
    IoInfo: IO_COUNTERS,
    ProcessMemoryLimit: SIZE_T,
    JobMemoryLimit: SIZE_T,
    PeakProcessMemoryUsed: SIZE_T,
    PeakJobMemoryUsed: SIZE_T,
}

#[repr(C)]
struct IO_COUNTERS {
    ReadOperationCount: ULONGLONG,
    WriteOperationCount: ULONGLONG,
    OtherOperationCount: ULONGLONG,
    ReadTransferCount: ULONGLONG,
    WriteTransferCount: ULONGLONG,
    OtherTransferCount: ULONGLONG,
}

#[repr(C)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION {
    PerProcessUserTimeLimit: LARGE_INTEGER,
    PerJobUserTimeLimit: LARGE_INTEGER,
    LimitFlags: DWORD,
    MinimumWorkingsetSize: SIZE_T,
    MaximumWorkingsetSize: SIZE_T,
    ActiveProcessLimit: DWORD,
    Affinity: ULONG_PTR,
    PriorityClass: DWORD,
    SchedulingClass: DWORD,
}

pub unsafe fn setup() {
    // Tell Windows to not show any UI on errors (such as not finding a required dll
    // during startup or terminating abnormally).  This is important for running tests,
    // since some of them use abnormal termination by design.
    // This mode is inherited by all child processes.
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);

    // Create a new job object for us to use
    let job = CreateJobObjectW(0 as *mut _, 0 as *const _);
    assert!(job != 0 as *mut _, "{}", io::Error::last_os_error());

    // Indicate that when all handles to the job object are gone that all
    // process in the object should be killed. Note that this includes our
    // entire process tree by default because we've added ourselves and our
    // children will reside in the job by default.
    let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
    info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    let r = SetInformationJobObject(job,
                                    JobObjectExtendedLimitInformation,
                                    &mut info as *mut _ as LPVOID,
                                    mem::size_of_val(&info) as DWORD);
    assert!(r != 0, "{}", io::Error::last_os_error());

    // Assign our process to this job object. Note that if this fails, one very
    // likely reason is that we are ourselves already in a job object! This can
    // happen on the build bots that we've got for Windows, or if just anyone
    // else is instrumenting the build. In this case we just bail out
    // immediately and assume that they take care of it.
    //
    // Also note that nested jobs (why this might fail) are supported in recent
    // versions of Windows, but the version of Windows that our bots are running
    // at least don't support nested job objects.
    let r = AssignProcessToJobObject(job, GetCurrentProcess());
    if r == 0 {
        CloseHandle(job);
        return
    }

    // If we've got a parent process (e.g. the python script that called us)
    // then move ownership of this job object up to them. That way if the python
    // script is killed (e.g. via ctrl-c) then we'll all be torn down.
    //
    // If we don't have a parent (e.g. this was run directly) then we
    // intentionally leak the job object handle. When our process exits
    // (normally or abnormally) it will close the handle implicitly, causing all
    // processes in the job to be cleaned up.
    let pid = match env::var("BOOTSTRAP_PARENT_ID") {
        Ok(s) => s,
        Err(..) => return,
    };

    let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
    assert!(parent != 0 as *mut _, "{}", io::Error::last_os_error());
    let mut parent_handle = 0 as *mut _;
    let r = DuplicateHandle(GetCurrentProcess(), job,
                            parent, &mut parent_handle,
                            0, FALSE, DUPLICATE_SAME_ACCESS);

    // If this failed, well at least we tried! An example of DuplicateHandle
    // failing in the past has been when the wrong python2 package spawed this
    // build system (e.g. the `python2` package in MSYS instead of
    // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
    // mode" here is that we only clean everything up when the build system
    // dies, not when the python parent does, so not too bad.
    if r != 0 {
        CloseHandle(job);
    }
}
<|endoftext|>


<commit_before>#![allow(missing_docs)] //TODO

use glutin::{MouseButton, MouseScrollDelta, ElementState, VirtualKeyCode as Key};
use mint;

use std::collections::HashSet;
use std::time;

const PIXELS_PER_LINE: f32 = 38.0;

pub type TimerDuration = f32;

#[allow(dead_code)]
struct InputState {
    time_moment: time::Instant,
    is_focused: bool,
    keys_pressed: HashSet<Key>,
    mouse_pressed: HashSet<MouseButton>,
    mouse_pos: mint::Point2<f32>,
}

struct InputDiff {
    time_delta: TimerDuration,
    keys_hit: Vec<Key>,
    mouse_moves: Vec<mint::Vector2<f32>>,
    mouse_hit: Vec<MouseButton>,
    mouse_wheel: Vec<f32>,
}

pub struct Input(InputState, InputDiff);

impl Input {
    pub fn new() -> Self {
        let state = InputState {
            time_moment: time::Instant::now(),
            is_focused: true,
            keys_pressed: HashSet::new(),
            mouse_pressed: HashSet::new(),
            mouse_pos: [0.0; 2].into(),
        };
        let diff = InputDiff {
            time_delta: 0.0,
            keys_hit: Vec::new(),
            mouse_moves: Vec::new(),
            mouse_hit: Vec::new(),
            mouse_wheel: Vec::new(),
        };
        Input(state, diff)
    }

    pub fn reset(&mut self) {
        let now = time::Instant::now();
        let dt = now - self.0.time_moment;
        self.0.time_moment = now;
        self.1.time_delta = dt.as_secs() as f32 + 1e-9 * dt.subsec_nanos() as f32;
        self.1.keys_hit.clear();
        self.1.mouse_moves.clear();
        self.1.mouse_hit.clear();
        self.1.mouse_wheel.clear();
    }

    pub fn time(&self) -> Timer {
        Timer {
            start: self.0.time_moment
        }
    }

    pub fn get_time_delta(&self) -> f32 {
        self.1.time_delta
    }

    pub fn get_mouse_pos(&self) -> mint::Point2<f32> {
        self.0.mouse_pos
    }

    pub fn get_mouse_wheel_movements(&self) -> &[f32] {
        &self.1.mouse_wheel[..]
    }

    pub fn get_mouse_wheel(&self) -> f32 {
        self.1.mouse_wheel.iter().sum()
    }

    pub fn keyboard_input(&mut self, state: ElementState, key: Key) {
        match state {
            ElementState::Pressed => {
                self.0.keys_pressed.insert(key);
                self.1.keys_hit.push(key);
            }
            ElementState::Released => {
                self.0.keys_pressed.remove(&key);
            }
        }
    }

    pub fn mouse_input(&mut self, state: ElementState, button: MouseButton) {
        match state {
            ElementState::Pressed => {
                self.0.mouse_pressed.insert(button);
                self.1.mouse_hit.push(button);
            }
            ElementState::Released => {
                self.0.mouse_pressed.remove(&button);
            }
        }
    }

    pub fn mouse_moved(&mut self, pos: mint::Point2<f32>) {
        self.1.mouse_moves.push(mint::Vector2 {
            x: pos.x - self.0.mouse_pos.x,
            y: pos.y - self.0.mouse_pos.y,
        });
        self.0.mouse_pos = pos;
    }

    pub fn mouse_wheel(&mut self, delta: MouseScrollDelta) {
        self.1.mouse_wheel.push(match delta {
            MouseScrollDelta::LineDelta(_, y) => y * PIXELS_PER_LINE,
            MouseScrollDelta::PixelDelta(_, y) => y,
        });
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Timer {
    start: time::Instant,
}

impl Timer {
    pub fn get(&self, input: &Input) -> TimerDuration {
        let dt = input.0.time_moment - self.start;
        dt.as_secs() as f32 + 1e-9 * dt.subsec_nanos() as f32
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Button {
    Key(Key),
    Mouse(MouseButton),
}

pub const KEY_ESCAPE: Button = Button::Key(Key::Escape);
pub const KEY_SPACE: Button = Button::Key(Key::Space);
pub const MOUSE_LEFT: Button = Button::Mouse(MouseButton::Left);
pub const MOUSE_RIGHT: Button = Button::Mouse(MouseButton::Right);

impl Button {
    pub fn count_hits(&self, input: &Input) -> u8 {
        use std::u8::MAX;
        match *self {
            Button::Key(button) => input.1.keys_hit.iter().filter(|&&key| key == button).take(MAX as usize).count() as u8,
            Button::Mouse(button) => input.1.mouse_hit.iter().filter(|&&key| key == button).take(MAX as usize).count() as u8,
        }
    }

    pub fn is_hit(&self, input: &Input) -> bool {
        match *self {
            Button::Key(button) => input.0.keys_pressed.contains(&button),
            Button::Mouse(button) => input.0.mouse_pressed.contains(&button),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct KeyAxis {
    pub neg: Key,
    pub pos: Key,
}

pub const AXIS_LEFT_RIGHT: KeyAxis = KeyAxis{ neg: Key::Left, pos: Key::Right };
pub const AXIS_DOWN_UP: KeyAxis = KeyAxis{ neg: Key::Down, pos: Key::Up };

impl KeyAxis {
    pub fn delta_hits(&self, input: &Input) -> Option<i8> {
        let (mut pos, mut neg) = (0, 0);
        for &key in input.1.keys_hit.iter() {
            if key == self.neg { neg += 1 }
            if key == self.pos { pos += 1 }
        }
        if pos + neg != 0 {
            Some(pos - neg)
        } else {
            None
        }
    }

    pub fn timed(&self, input: &Input) -> Option<TimerDuration> {
        let is_pos = input.0.keys_pressed.contains(&self.pos);
        let is_neg = input.0.keys_pressed.contains(&self.neg);
        if is_pos && is_neg {
            Some(0.0)
        } else if is_pos {
            Some(input.1.time_delta)
        } else if is_neg {
            Some(-input.1.time_delta)
        } else {
            None
        }
    }
}
<commit_msg>Expose mouse_moves<commit_after>#![allow(missing_docs)] //TODO

use glutin::{MouseButton, MouseScrollDelta, ElementState, VirtualKeyCode as Key};
use mint;

use std::collections::HashSet;
use std::time;

const PIXELS_PER_LINE: f32 = 38.0;

pub type TimerDuration = f32;

#[allow(dead_code)]
struct InputState {
    time_moment: time::Instant,
    is_focused: bool,
    keys_pressed: HashSet<Key>,
    mouse_pressed: HashSet<MouseButton>,
    mouse_pos: mint::Point2<f32>,
}

struct InputDiff {
    time_delta: TimerDuration,
    keys_hit: Vec<Key>,
    mouse_moves: Vec<mint::Vector2<f32>>,
    mouse_hit: Vec<MouseButton>,
    mouse_wheel: Vec<f32>,
}

pub struct Input(InputState, InputDiff);

impl Input {
    pub fn new() -> Self {
        let state = InputState {
            time_moment: time::Instant::now(),
            is_focused: true,
            keys_pressed: HashSet::new(),
            mouse_pressed: HashSet::new(),
            mouse_pos: [0.0; 2].into(),
        };
        let diff = InputDiff {
            time_delta: 0.0,
            keys_hit: Vec::new(),
            mouse_moves: Vec::new(),
            mouse_hit: Vec::new(),
            mouse_wheel: Vec::new(),
        };
        Input(state, diff)
    }

    pub fn reset(&mut self) {
        let now = time::Instant::now();
        let dt = now - self.0.time_moment;
        self.0.time_moment = now;
        self.1.time_delta = dt.as_secs() as f32 + 1e-9 * dt.subsec_nanos() as f32;
        self.1.keys_hit.clear();
        self.1.mouse_moves.clear();
        self.1.mouse_hit.clear();
        self.1.mouse_wheel.clear();
    }

    pub fn time(&self) -> Timer {
        Timer {
            start: self.0.time_moment
        }
    }

    pub fn get_time_delta(&self) -> f32 {
        self.1.time_delta
    }

    pub fn get_mouse_pos(&self) -> mint::Point2<f32> {
        self.0.mouse_pos
    }

    pub fn get_mouse_wheel_movements(&self) -> &[f32] {
        &self.1.mouse_wheel[..]
    }

    pub fn get_mouse_wheel(&self) -> f32 {
        self.1.mouse_wheel.iter().sum()
    }

    pub fn get_mouse_movements(&self) -> &[mint::Vector2<f32>] {
        &self.1.mouse_moves[..]
    }

    pub fn get_mouse_delta(&self) -> mint::Vector2<f32> {
        // TEMP
        use cgmath::Vector2;
        let delta: Vector2<f32> = self.1.mouse_moves.iter()
            .map(|v| Vector2::new(v.x, v.y))
            .sum();
        [delta.x, delta.y].into()
    }

    pub fn keyboard_input(&mut self, state: ElementState, key: Key) {
        match state {
            ElementState::Pressed => {
                self.0.keys_pressed.insert(key);
                self.1.keys_hit.push(key);
            }
            ElementState::Released => {
                self.0.keys_pressed.remove(&key);
            }
        }
    }

    pub fn mouse_input(&mut self, state: ElementState, button: MouseButton) {
        match state {
            ElementState::Pressed => {
                self.0.mouse_pressed.insert(button);
                self.1.mouse_hit.push(button);
            }
            ElementState::Released => {
                self.0.mouse_pressed.remove(&button);
            }
        }
    }

    pub fn mouse_moved(&mut self, pos: mint::Point2<f32>) {
        self.1.mouse_moves.push(mint::Vector2 {
            x: pos.x - self.0.mouse_pos.x,
            y: pos.y - self.0.mouse_pos.y,
        });
        self.0.mouse_pos = pos;
    }

    pub fn mouse_wheel(&mut self, delta: MouseScrollDelta) {
        self.1.mouse_wheel.push(match delta {
            MouseScrollDelta::LineDelta(_, y) => y * PIXELS_PER_LINE,
            MouseScrollDelta::PixelDelta(_, y) => y,
        });
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Timer {
    start: time::Instant,
}

impl Timer {
    pub fn get(&self, input: &Input) -> TimerDuration {
        let dt = input.0.time_moment - self.start;
        dt.as_secs() as f32 + 1e-9 * dt.subsec_nanos() as f32
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Button {
    Key(Key),
    Mouse(MouseButton),
}

pub const KEY_ESCAPE: Button = Button::Key(Key::Escape);
pub const KEY_SPACE: Button = Button::Key(Key::Space);
pub const MOUSE_LEFT: Button = Button::Mouse(MouseButton::Left);
pub const MOUSE_RIGHT: Button = Button::Mouse(MouseButton::Right);

impl Button {
    pub fn count_hits(&self, input: &Input) -> u8 {
        use std::u8::MAX;
        match *self {
            Button::Key(button) => input.1.keys_hit.iter().filter(|&&key| key == button).take(MAX as usize).count() as u8,
            Button::Mouse(button) => input.1.mouse_hit.iter().filter(|&&key| key == button).take(MAX as usize).count() as u8,
        }
    }

    pub fn is_hit(&self, input: &Input) -> bool {
        match *self {
            Button::Key(button) => input.0.keys_pressed.contains(&button),
            Button::Mouse(button) => input.0.mouse_pressed.contains(&button),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct KeyAxis {
    pub neg: Key,
    pub pos: Key,
}

pub const AXIS_LEFT_RIGHT: KeyAxis = KeyAxis{ neg: Key::Left, pos: Key::Right };
pub const AXIS_DOWN_UP: KeyAxis = KeyAxis{ neg: Key::Down, pos: Key::Up };

impl KeyAxis {
    pub fn delta_hits(&self, input: &Input) -> Option<i8> {
        let (mut pos, mut neg) = (0, 0);
        for &key in input.1.keys_hit.iter() {
            if key == self.neg { neg += 1 }
            if key == self.pos { pos += 1 }
        }
        if pos + neg != 0 {
            Some(pos - neg)
        } else {
            None
        }
    }

    pub fn timed(&self, input: &Input) -> Option<TimerDuration> {
        let is_pos = input.0.keys_pressed.contains(&self.pos);
        let is_neg = input.0.keys_pressed.contains(&self.neg);
        if is_pos && is_neg {
            Some(0.0)
        } else if is_pos {
            Some(input.1.time_delta)
        } else if is_neg {
            Some(-input.1.time_delta)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
            .map(|_| ())
    }

}

<commit_msg>Add some debug output if commit worked<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>use gf256::Gf256;
use poly::Poly;

/// Evaluates an interpolated polynomial at `Gf256::zero()` where
/// the polynomial is determined using barycentric Lagrange
/// interpolation based on the given `points` in
/// the G(2^8) Galois field.
pub(crate) fn interpolate_at(k: u8, points: &[(u8, u8)]) -> u8 {
    barycentric_interpolate_at(k as usize, points)
}

/// Barycentric Lagrange interpolation algorithm from "Polynomial
/// Interpolation: Langrange vs Newton" by Wilhelm Werner. Evaluates
/// the polynomial at `Gf256::zero()`.
#[inline]
fn barycentric_interpolate_at(k: usize, points: &[(u8, u8)]) -> u8 {
    // Compute the barycentric weights `w`.
    let mut w = vec![Gf256::zero(); k];
    w[0] = Gf256::one();

    let mut x = Vec::with_capacity(k);
    x.push(Gf256::from_byte(points[0].0));

    for i in 1..k {
        x.push(Gf256::from_byte(points[i].0));
        for j in 0..i {
            let delta = x[j] - x[i];
            assert_ne!(delta.poly, 0, "Duplicate shares");
            w[j] /= delta;
            w[i] -= w[j];
        }
    }

    // Evaluate the second or "true" form of the barycentric
    // interpolation formula at `Gf256::zero()`.
    let (mut num, mut denom) = (Gf256::zero(), Gf256::zero());
    for i in 0..k {
        assert_ne!(x[i].poly, 0, "Invalid share x = 0");
        let diff = w[i] / x[i];
        num += diff * Gf256::from_byte(points[i].1);
        denom += diff;
    }

    (num / denom).to_byte()
}

/// Computeds the coefficient of the Lagrange polynomial interpolated
/// from the given `points`, in the G(2^8) Galois field.
pub(crate) fn interpolate(points: &[(Gf256, Gf256)]) -> Poly {
    let len = points.len();

    let mut poly = vec![Gf256::zero(); len];

    for &(x, y) in points {
        assert_ne!(x.poly, 0, "Invalid share x = 0");
        let mut coeffs = vec![Gf256::zero(); len];
        coeffs[0] = y;

        let mut prod = Gf256::one();
        for &(x1, _) in points {
            if x != x1 {
                prod *= x - x1;

                let mut prec = Gf256::zero();
                coeffs = coeffs
                    .into_iter()
                    .map(|coeff| {
                        let new_coeff = coeff * (-x1) + prec;
                        prec = coeff;
                        new_coeff
                    })
                    .collect();
            }
        }

        poly = poly.iter()
            .zip(coeffs.iter())
            .map(|(&old_coeff, &add)| old_coeff + add / prod)
            .collect();
    }

    Poly::new(poly)
}

#[cfg(test)]
#[allow(trivial_casts)]
mod tests {

    use std;
    use super::*;
    use gf256::*;
    use quickcheck::*;

    quickcheck! {

        fn interpolate_evaluate_at_works(ys: Vec<Gf256>) -> TestResult {
            if ys.is_empty() || ys.len() > std::u8::MAX as usize {
                return TestResult::discard();
            }

            let points = ys.into_iter()
                           .zip(1..std::u8::MAX)
                           .map(|(y, x)| (gf256!(x), y))
                           .collect::<Vec<_>>();
            let poly = interpolate(&points);

            for (x, y) in points {
                if poly.evaluate_at(x) != y {
                    return TestResult::failed();
                }
            }

            TestResult::passed()
        }

        fn interpolate_evaluate_at_0_eq_evaluate_at(ys: Vec<u8>) -> TestResult {
            if ys.len() > std::u8::MAX as usize {
                return TestResult::discard();
            }

            let points = ys.into_iter()
                           .zip(1..std::u8::MAX)
                           .map(|(y, x)| (x, y))
                           .collect::<Vec<_>>();

            let elems = points
                .iter()
                .map(|&(x, y)| (gf256!(x), gf256!(y)))
                .collect::<Vec<_>>();

            let poly = interpolate(&elems);

            let equals = poly.evaluate_at(Gf256::zero()).to_byte()
                == interpolate_at(points.len() as u8, points.as_slice());

            TestResult::from_bool(equals)
        }

    }

}
<commit_msg>Ensure there is at least one point in QuickCheck tests<commit_after>use gf256::Gf256;
use poly::Poly;

/// Evaluates an interpolated polynomial at `Gf256::zero()` where
/// the polynomial is determined using barycentric Lagrange
/// interpolation based on the given `points` in
/// the G(2^8) Galois field.
pub(crate) fn interpolate_at(k: u8, points: &[(u8, u8)]) -> u8 {
    barycentric_interpolate_at(k as usize, points)
}

/// Barycentric Lagrange interpolation algorithm from "Polynomial
/// Interpolation: Langrange vs Newton" by Wilhelm Werner. Evaluates
/// the polynomial at `Gf256::zero()`.
#[inline]
fn barycentric_interpolate_at(k: usize, points: &[(u8, u8)]) -> u8 {
    // Compute the barycentric weights `w`.
    let mut w = vec![Gf256::zero(); k];
    w[0] = Gf256::one();

    let mut x = Vec::with_capacity(k);
    x.push(Gf256::from_byte(points[0].0));

    for i in 1..k {
        x.push(Gf256::from_byte(points[i].0));
        for j in 0..i {
            let delta = x[j] - x[i];
            assert_ne!(delta.poly, 0, "Duplicate shares");
            w[j] /= delta;
            w[i] -= w[j];
        }
    }

    // Evaluate the second or "true" form of the barycentric
    // interpolation formula at `Gf256::zero()`.
    let (mut num, mut denom) = (Gf256::zero(), Gf256::zero());
    for i in 0..k {
        assert_ne!(x[i].poly, 0, "Invalid share x = 0");
        let diff = w[i] / x[i];
        num += diff * Gf256::from_byte(points[i].1);
        denom += diff;
    }

    (num / denom).to_byte()
}

/// Computeds the coefficient of the Lagrange polynomial interpolated
/// from the given `points`, in the G(2^8) Galois field.
pub(crate) fn interpolate(points: &[(Gf256, Gf256)]) -> Poly {
    let len = points.len();

    let mut poly = vec![Gf256::zero(); len];

    for &(x, y) in points {
        assert_ne!(x.poly, 0, "Invalid share x = 0");
        let mut coeffs = vec![Gf256::zero(); len];
        coeffs[0] = y;

        let mut prod = Gf256::one();
        for &(x1, _) in points {
            if x != x1 {
                prod *= x - x1;

                let mut prec = Gf256::zero();
                coeffs = coeffs
                    .into_iter()
                    .map(|coeff| {
                        let new_coeff = coeff * (-x1) + prec;
                        prec = coeff;
                        new_coeff
                    })
                    .collect();
            }
        }

        poly = poly.iter()
            .zip(coeffs.iter())
            .map(|(&old_coeff, &add)| old_coeff + add / prod)
            .collect();
    }

    Poly::new(poly)
}

#[cfg(test)]
#[allow(trivial_casts)]
mod tests {

    use std;
    use super::*;
    use gf256::*;
    use quickcheck::*;

    quickcheck! {

        fn interpolate_evaluate_at_works(ys: Vec<Gf256>) -> TestResult {
            if ys.is_empty() || ys.len() > std::u8::MAX as usize {
                return TestResult::discard();
            }

            let points = ys.into_iter()
                           .zip(1..std::u8::MAX)
                           .map(|(y, x)| (gf256!(x), y))
                           .collect::<Vec<_>>();
            let poly = interpolate(&points);

            for (x, y) in points {
                if poly.evaluate_at(x) != y {
                    return TestResult::failed();
                }
            }

            TestResult::passed()
        }

        fn interpolate_evaluate_at_0_eq_evaluate_at(ys: Vec<u8>) -> TestResult {
            if ys.is_empty() || ys.len() > std::u8::MAX as usize {
                return TestResult::discard();
            }

            let points = ys.into_iter()
                           .zip(1..std::u8::MAX)
                           .map(|(y, x)| (x, y))
                           .collect::<Vec<_>>();

            let elems = points
                .iter()
                .map(|&(x, y)| (gf256!(x), gf256!(y)))
                .collect::<Vec<_>>();

            let poly = interpolate(&elems);

            let equals = poly.evaluate_at(Gf256::zero()).to_byte()
                == interpolate_at(points.len() as u8, points.as_slice());

            TestResult::from_bool(equals)
        }

    }

}
<|endoftext|>


<commit_before>use std::path::MAIN_SEPARATOR;
use libc::c_char;
use std::ffi::{CStr,CString};
use std::str;

#[allow(dead_code)]
fn rubyish_basename(string: &str, globish_string: &str) -> String {
  let result = if globish_string == ".*" {
    let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
    let index = base.rfind(".");
    let (first, _) = base.split_at(index.unwrap());
    first
  } else {
    if &string[string.len()-globish_string.len()..] == globish_string {
      &string[0..string.len()-globish_string.len()]
    } else {
      string
    }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
  };
  result.to_string()
}

#[test]
fn it_chomps_strings_correctly(){
  assert_eq!(rubyish_basename("","")                           , "");
  assert_eq!(rubyish_basename("ruby","")                       , "ruby");
  assert_eq!(rubyish_basename("ruby.rb",".rb")                 , "ruby");
  assert_eq!(rubyish_basename("ruby.rb",".*")                  , "ruby");
  assert_eq!(rubyish_basename(".ruby/ruby.rb",".rb")           , "ruby");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".rb")       , "ruby.rb.swp");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".swp")      , "ruby.rb");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".*")        , "ruby.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp","")           , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "*")         , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".")         , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".swp")      , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw*")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*")    , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s*")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s**")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".**")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*.*")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.swp")   , "asdf");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*p")   , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.s*p")   , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.sw*p")  , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*b.sw*p") , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "rb.swp")    , "asdf.");
}

//#[no_mangle]
pub extern fn basename(str_pth: *const c_char, comp_ext: *const c_char) -> *const c_char {
  let c_str1 = unsafe {
    assert!(!str_pth.is_null());
    CStr::from_ptr(str_pth)
  };
  let c_str2 = unsafe {
    assert!(!comp_ext.is_null());
    CStr::from_ptr(comp_ext)
  };
  let string         = str::from_utf8(c_str1.to_bytes()).unwrap();
  let globish_string = str::from_utf8(c_str2.to_bytes()).unwrap();

  let result = if globish_string == ".*" {
    let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
    let index = base.rfind(".");
    let (first, _) = base.split_at(index.unwrap());
    first
  } else {
    if &string[string.len()-globish_string.len()..] == globish_string {
      &string[0..string.len()-globish_string.len()]
    } else {
      string
    }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
  };
  CString::new(result).unwrap().into_raw()
}
<commit_msg>Fix issue #44 for Windows<commit_after>use std::path::MAIN_SEPARATOR;
use libc::c_char;
use std::ffi::{CStr,CString};
use std::str;

#[allow(dead_code)]
fn rubyish_basename(string: &str, globish_string: &str) -> String {
  let result = if globish_string == ".*" {
    let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
    let index = base.rfind(".");
    let (first, _) = base.split_at(index.unwrap());
    first
  } else {
    if &string[string.len()-globish_string.len()..] == globish_string {
      &string[0..string.len()-globish_string.len()]
    } else {
      string
    }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
  };
  result.to_string()
}

#[test]
fn it_chomps_strings_correctly(){
  assert_eq!(rubyish_basename("","")                           , "");
  assert_eq!(rubyish_basename("ruby","")                       , "ruby");
  assert_eq!(rubyish_basename("ruby.rb",".rb")                 , "ruby");
  assert_eq!(rubyish_basename("ruby.rb",".*")                  , "ruby");
  assert_eq!(rubyish_basename(".ruby/ruby.rb",".rb")           , "ruby");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".rb")       , "ruby.rb.swp");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".swp")      , "ruby.rb");
  assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".*")        , "ruby.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp","")           , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "*")         , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".")         , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".swp")      , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw*")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*")    , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s*")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s**")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".**")       , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*")        , "asdf.rb");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*.*")      , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.swp")   , "asdf");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*p")   , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.s*p")   , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.sw*p")  , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*b.sw*p") , "asdf.rb.swp");
  assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "rb.swp")    , "asdf.");
}

#[no_mangle]
pub extern fn basename(str_pth: *const c_char, comp_ext: *const c_char) -> *const c_char {
  let c_str1 = unsafe {
    assert!(!str_pth.is_null());
    CStr::from_ptr(str_pth)
  };
  let c_str2 = unsafe {
    assert!(!comp_ext.is_null());
    CStr::from_ptr(comp_ext)
  };
  let string         = str::from_utf8(c_str1.to_bytes()).unwrap();
  let globish_string = str::from_utf8(c_str2.to_bytes()).unwrap();

  let result = if globish_string == ".*" {
    let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
    let index = base.rfind(".");
    let (first, _) = base.split_at(index.unwrap());
    first
  } else {
    if &string[string.len()-globish_string.len()..] == globish_string {
      &string[0..string.len()-globish_string.len()]
    } else {
      string
    }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
  };
  CString::new(result).unwrap().into_raw()
}
<|endoftext|>


<commit_before>extern crate array_tool;
use path_parsing::extract_last_path_segment;
use self::array_tool::string::Squeeze;

pub fn basename(pth: &str, ext: &str) -> String {
  // Known edge case
  if &pth.squeeze("/")[..] == "/" { return "/".to_string(); }

  let mut name = extract_last_path_segment(pth);

  if ext == ".*" {
    if let Some(dot_i) = name.rfind('.') {
      name = &name[0..dot_i];
    }
  } else if name.ends_with(ext) {
    name = &name[..name.len() - ext.len()];
  };
  name.to_string()
}

<commit_msg>Optimize edge-case check in basename<commit_after>use std::path::MAIN_SEPARATOR;
use path_parsing::extract_last_path_segment;

pub fn basename(pth: &str, ext: &str) -> String {
  // Known edge case
  if !pth.is_empty() && pth.bytes().all(|b| b == (MAIN_SEPARATOR as u8)) {
    return MAIN_SEPARATOR.to_string();
  }

  let mut name = extract_last_path_segment(pth);

  if ext == ".*" {
    if let Some(dot_i) = name.rfind('.') {
      name = &name[0..dot_i];
    }
  } else if name.ends_with(ext) {
    name = &name[..name.len() - ext.len()];
  };
  name.to_string()
}

#[test]
fn edge_case_all_seps() {
  assert_eq!("/", basename("///", ".*"));
}
<|endoftext|>


<commit_before>fn main() {

}
<commit_msg>implement echo<commit_after>use std::env;

fn main() {
    for arg in env::args().skip(1) {
        print!("{} ", arg);
    }
    print!("\n");
}
<|endoftext|>


<commit_before>use std::collections::LinkedList;

use token::Token;

use std::cell::Cell;
use std::rc::Rc;
use regex::Regex;
use context::Context;
use token::Posn;

pub trait ESCharExt {
    fn is_es_newline(self) -> bool;
    fn is_es_whitespace(self) -> bool;
}

impl ESCharExt for char {
    fn is_es_newline(self) -> bool {
        match self {
            '\u{000a}' | '\u{000d}' | '\u{2028}' | '\u{2029}' => true,
            _ => false
        }
    }

    fn is_es_whitespace(self) -> bool {
        match self {
              '\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}'
            | '\u{1680}' | '\u{2000}' | '\u{2001}' | '\u{2002}' | '\u{2003}' | '\u{2004}'
            | '\u{2005}' | '\u{2006}' | '\u{2009}' | '\u{200a}' | '\u{202f}' | '\u{205f}'
            | '\u{3000}' | '\u{feff}' => true,
            _ => false
        }
    }
}

struct LineOrientedReader<I> {
    chars: I,
    curr_char: Option<char>,
    next_char: Option<char>,
    curr_posn: Posn
}

impl<I> LineOrientedReader<I> where I: Iterator<Item=char> {
    pub fn new(mut chars: I) -> LineOrientedReader<I> {
        let curr_char = chars.next();
        let next_char = if curr_char.is_some() { chars.next() } else { None };
        LineOrientedReader {
            chars: chars,
            curr_char: curr_char,
            next_char: next_char,
            curr_posn: Posn {
                offset: 0,
                line: 0,
                column: 0
            }
        }
    }

    pub fn curr_char(&mut self) -> Option<char> { self.curr_char }
    pub fn curr_posn(&mut self) -> Posn { self.curr_posn }

    pub fn bump(&mut self) {
        let curr_char = self.next_char;
        let next_char = if curr_char.is_some() { self.chars.next() } else { None };

        self.curr_char = curr_char;
        self.next_char = next_char;

        if (curr_char == Some('\r') && next_char != Some('\n')) ||
           curr_char == Some('\n') ||
           curr_char == Some('\u{2028}') ||
           curr_char == Some('\u{2029}') {
            self.curr_posn.line += 1;
            self.curr_posn.column = 0;
        } else {
            self.curr_posn.column += 1;
        }

        self.curr_posn.offset += 1;
    }
}

struct TokenBuffer {
    tokens: LinkedList<Token>
}

impl TokenBuffer {
    fn new() -> TokenBuffer {
        TokenBuffer {
            tokens: LinkedList::new()
        }
    }

    fn ensure_nonempty<F: FnMut() -> Token>(&mut self, get: F) {
        if self.tokens.len() == 0 {
            self.tokens.push_back(get());
        }
    }

    fn read_token(&mut self) -> Token {
        assert!(self.tokens.len() > 0);
        self.tokens.pop_front().unwrap()
    }
}

pub struct Lexer<I> {
    reader: LineOrientedReader<I>,
    cx: Rc<Cell<Context>>,
    lookahead: TokenBuffer
}

impl<I> Lexer<I> where I: Iterator<Item=char> {
    // constructor

    pub fn new(chars: I, cx: Rc<Cell<Context>>) -> Lexer<I> {
        Lexer {
            reader: LineOrientedReader::new(chars),
            cx: cx,
            lookahead: TokenBuffer::new()
        }
    }

    // public methods

    pub fn read_token(&mut self) -> Token {
        self.lookahead.ensure_nonempty(|| {
            self.read_next_token()
        });
        unimplemented!()
    }

    // private methods

    fn read_next_token(&mut self) -> Token {
        unimplemented!()
    }

    fn is_whitespace(&mut self) -> bool {
        match self.reader.curr_char() {
            Some(ch) => ch.is_es_whitespace(),
            None => false
        }
    }

    fn eat(&mut self) -> Option<char> {
        let ch = self.reader.curr_char();
        self.reader.bump();
        ch
    }

    fn skip_whitespace(&mut self) {
        while self.is_whitespace() {
            self.reader.bump();
        }
    }
}

impl<I> Iterator for Lexer<I> where I: Iterator<Item=char> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.skip_whitespace();
        self.eat()
            .map(|ch| {
                match ch {
                    '1' => Token::DecimalInt(String::from_str("1")),
                    '+' => Token::Plus,
                    _ => Token::Error(ch)
                }
            })
    }
}
<commit_msg>make the param mut per Carl's suggestion; now we have a real borrowing bug in read_token src/lexer.rs:125:40: 127:10 error: closure requires unique access to `self` but `self.lookahead` is already borrowed src/lexer.rs:125         self.lookahead.ensure_nonempty(|| { src/lexer.rs:126             self.read_next_token() src/lexer.rs:127         }); src/lexer.rs:126:13: 126:17 note: borrow occurs due to use of `self` in closure src/lexer.rs:126             self.read_next_token()                              ^~~~ src/lexer.rs:125:9: 125:23 note: previous borrow of `self.lookahead` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `self.lookahead` until the borrow ends src/lexer.rs:125         self.lookahead.ensure_nonempty(|| {                          ^~~~~~~~~~~~~~ src/lexer.rs:127:11: 127:11 note: previous borrow ends here src/lexer.rs:125         self.lookahead.ensure_nonempty(|| { src/lexer.rs:126             self.read_next_token() src/lexer.rs:127         });<commit_after>use std::collections::LinkedList;

use token::Token;

use std::cell::Cell;
use std::rc::Rc;
use regex::Regex;
use context::Context;
use token::Posn;

pub trait ESCharExt {
    fn is_es_newline(self) -> bool;
    fn is_es_whitespace(self) -> bool;
}

impl ESCharExt for char {
    fn is_es_newline(self) -> bool {
        match self {
            '\u{000a}' | '\u{000d}' | '\u{2028}' | '\u{2029}' => true,
            _ => false
        }
    }

    fn is_es_whitespace(self) -> bool {
        match self {
              '\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}'
            | '\u{1680}' | '\u{2000}' | '\u{2001}' | '\u{2002}' | '\u{2003}' | '\u{2004}'
            | '\u{2005}' | '\u{2006}' | '\u{2009}' | '\u{200a}' | '\u{202f}' | '\u{205f}'
            | '\u{3000}' | '\u{feff}' => true,
            _ => false
        }
    }
}

struct LineOrientedReader<I> {
    chars: I,
    curr_char: Option<char>,
    next_char: Option<char>,
    curr_posn: Posn
}

impl<I> LineOrientedReader<I> where I: Iterator<Item=char> {
    pub fn new(mut chars: I) -> LineOrientedReader<I> {
        let curr_char = chars.next();
        let next_char = if curr_char.is_some() { chars.next() } else { None };
        LineOrientedReader {
            chars: chars,
            curr_char: curr_char,
            next_char: next_char,
            curr_posn: Posn {
                offset: 0,
                line: 0,
                column: 0
            }
        }
    }

    pub fn curr_char(&mut self) -> Option<char> { self.curr_char }
    pub fn curr_posn(&mut self) -> Posn { self.curr_posn }

    pub fn bump(&mut self) {
        let curr_char = self.next_char;
        let next_char = if curr_char.is_some() { self.chars.next() } else { None };

        self.curr_char = curr_char;
        self.next_char = next_char;

        if (curr_char == Some('\r') && next_char != Some('\n')) ||
           curr_char == Some('\n') ||
           curr_char == Some('\u{2028}') ||
           curr_char == Some('\u{2029}') {
            self.curr_posn.line += 1;
            self.curr_posn.column = 0;
        } else {
            self.curr_posn.column += 1;
        }

        self.curr_posn.offset += 1;
    }
}

struct TokenBuffer {
    tokens: LinkedList<Token>
}

impl TokenBuffer {
    fn new() -> TokenBuffer {
        TokenBuffer {
            tokens: LinkedList::new()
        }
    }

    fn ensure_nonempty<F: FnMut() -> Token>(&mut self, mut get: F) {
        if self.tokens.len() == 0 {
            self.tokens.push_back(get());
        }
    }

    fn read_token(&mut self) -> Token {
        assert!(self.tokens.len() > 0);
        self.tokens.pop_front().unwrap()
    }
}

pub struct Lexer<I> {
    reader: LineOrientedReader<I>,
    cx: Rc<Cell<Context>>,
    lookahead: TokenBuffer
}

impl<I> Lexer<I> where I: Iterator<Item=char> {
    // constructor

    pub fn new(chars: I, cx: Rc<Cell<Context>>) -> Lexer<I> {
        Lexer {
            reader: LineOrientedReader::new(chars),
            cx: cx,
            lookahead: TokenBuffer::new()
        }
    }

    // public methods

    pub fn read_token(&mut self) -> Token {
        self.lookahead.ensure_nonempty(|| {
            self.read_next_token()
        });
        unimplemented!()
    }

    // private methods

    fn read_next_token(&mut self) -> Token {
        unimplemented!()
    }

    fn is_whitespace(&mut self) -> bool {
        match self.reader.curr_char() {
            Some(ch) => ch.is_es_whitespace(),
            None => false
        }
    }

    fn eat(&mut self) -> Option<char> {
        let ch = self.reader.curr_char();
        self.reader.bump();
        ch
    }

    fn skip_whitespace(&mut self) {
        while self.is_whitespace() {
            self.reader.bump();
        }
    }
}

impl<I> Iterator for Lexer<I> where I: Iterator<Item=char> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        self.skip_whitespace();
        self.eat()
            .map(|ch| {
                match ch {
                    '1' => Token::DecimalInt(String::from_str("1")),
                    '+' => Token::Plus,
                    _ => Token::Error(ch)
                }
            })
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, Eq, Hash, PartialEq, Clone)]
pub enum Symbol {
    OR,
    AND,
    XOR,
    NOT,
    RSHIFT,
    LSHIFT,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    OpenBracket,
    CloseBracket,
    Variable(String),
    Decimal(String), // Literal expression: 42
    Radix(String), // Non-Dec
    Operator(Symbol),
    Unknown(char),
}

pub type Tokens = Vec<(usize, Token)>;

fn identify_radix(position: usize, radix: String, tokens: &mut Tokens) {
    if !radix.parse::<i32>().is_err() {
        tokens.push((position, Token::Decimal(radix)));
    } else if radix.chars().all(|c| c.is_alphabetic()) {
        tokens.push((position, Token::Variable(radix)));
    } else {
        tokens.push((position, Token::Radix(radix)));
    }
}

pub fn lexer(line: &str) -> Tokens {
    let mut tokens = Tokens::new();

    let mut iterator = line.chars().enumerate().peekable();
    while let Some((position, character)) = iterator.next() {
        if character.is_whitespace() {
            continue;
        }

        match character {
            ')' => tokens.push((position, Token::CloseBracket)),
            '(' => tokens.push((position, Token::OpenBracket)),
            '^' => tokens.push((position, Token::Operator(Symbol::XOR))),
            '&' => tokens.push((position, Token::Operator(Symbol::AND))),
            '|' => tokens.push((position, Token::Operator(Symbol::OR))),
            '~' => tokens.push((position, Token::Operator(Symbol::NOT))),
            '>' => {
                let shift_position = position;
                match iterator.peek() {
                    Some(&(_, '>')) => {
                        iterator.next();
                        tokens.push((shift_position, Token::Operator(Symbol::RSHIFT)));
                    }
                    _ => tokens.push((shift_position, Token::Unknown(character))),
                }
            }
            '<' => {
                let shift_position = position;
                match iterator.peek() {
                    Some(&(_, '<')) => {
                        iterator.next();
                        tokens.push((shift_position, Token::Operator(Symbol::LSHIFT)));
                    }
                    _ => tokens.push((shift_position, Token::Unknown(character))),
                }
            }

            _ if character.is_alphanumeric() => {
                let radix_position = position;
                let mut radix = String::new();
                radix.push(character);
                while let Some(&(_, rx)) = iterator.peek() {
                    if !rx.is_alphanumeric() {
                        break;
                    }
                    iterator.next();
                    radix.push(rx);
                }
                identify_radix(radix_position, radix, &mut tokens);
            }
            _ => tokens.push((position, Token::Unknown(character))),
        }
    }

    tokens
}
<commit_msg>Change identify_radix to return a Token<commit_after>#[derive(Debug, Eq, Hash, PartialEq, Clone)]
pub enum Symbol {
    OR,
    AND,
    XOR,
    NOT,
    RSHIFT,
    LSHIFT,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    OpenBracket,
    CloseBracket,
    Variable(String),
    Decimal(String), // Literal expression: 42
    Radix(String), // Non-Dec
    Operator(Symbol),
    Unknown(char),
}

pub type Tokens = Vec<(usize, Token)>;

fn identify_radix(radix: String) -> Token {
    if !radix.parse::<i32>().is_err() {
        return Token::Decimal(radix);
    } else if radix.chars().all(|c| c.is_alphabetic()) {
        return Token::Variable(radix);
    } else {
        return Token::Radix(radix);
    }
}

pub fn lexer(line: &str) -> Tokens {
    let mut tokens = Tokens::new();

    let mut iterator = line.chars().enumerate().peekable();
    while let Some((position, character)) = iterator.next() {
        if character.is_whitespace() {
            continue;
        }

        match character {
            ')' => tokens.push((position, Token::CloseBracket)),
            '(' => tokens.push((position, Token::OpenBracket)),
            '^' => tokens.push((position, Token::Operator(Symbol::XOR))),
            '&' => tokens.push((position, Token::Operator(Symbol::AND))),
            '|' => tokens.push((position, Token::Operator(Symbol::OR))),
            '~' => tokens.push((position, Token::Operator(Symbol::NOT))),
            '>' => {
                let shift_position = position;
                match iterator.peek() {
                    Some(&(_, '>')) => {
                        iterator.next();
                        tokens.push((shift_position, Token::Operator(Symbol::RSHIFT)));
                    }
                    _ => tokens.push((shift_position, Token::Unknown(character))),
                }
            }
            '<' => {
                let shift_position = position;
                match iterator.peek() {
                    Some(&(_, '<')) => {
                        iterator.next();
                        tokens.push((shift_position, Token::Operator(Symbol::LSHIFT)));
                    }
                    _ => tokens.push((shift_position, Token::Unknown(character))),
                }
            }

            _ if character.is_alphanumeric() => {
                let radix_position = position;
                let mut radix = String::new();
                radix.push(character);
                while let Some(&(_, rx)) = iterator.peek() {
                    if !rx.is_alphanumeric() {
                        break;
                    }
                    iterator.next();
                    radix.push(rx);
                }
                tokens.push((radix_position, identify_radix(radix)));
            }
            _ => tokens.push((position, Token::Unknown(character))),
        }
    }

    tokens
}
<|endoftext|>


<commit_before>use super::{SpecifierClass, SpecifierStack, WebsocatConfiguration2};
use std::rc::Rc;
use std::str::FromStr;

#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
pub enum StdioUsageStatus {
    /// Does not use standard input or output at all
    None,
    /// Uses a reuser for connecting multiple peers at stdio, not distinguishing between IsItself and Indirectly
    WithReuser,
    /// Stdio wrapped into something (but not the reuser)
    Indirectly,
    /// Is the `-` or `stdio:` or `threadedstdio:` itself.
    IsItself,
}

trait ClassExt {
    fn is_stdio(&self) -> bool;
    fn is_reuser(&self) -> bool;
}

#[cfg_attr(rustfmt, rustfmt_skip)]
impl ClassExt for Rc<SpecifierClass> {
    fn is_stdio(&self) -> bool {
        [
            "StdioClass",
            "ThreadedStdioClass",
            "ThreadedStdioSubstituteClass",
        ].contains(&self.get_name())
    }
    fn is_reuser(&self) -> bool {
        [
            "ReuserClass",
            "BroadcastReuserClass",
        ].contains(&self.get_name())
    }
}

pub trait SpecifierStackExt {
    fn stdio_usage_status(&self) -> StdioUsageStatus;
    fn reuser_count(&self) -> usize;
    fn contains(&self, t: &'static str) -> bool;
    fn is_multiconnect(&self) -> bool;
    fn is_stream_oriented(&self) -> bool;
    fn insert_line_class_in_proper_place(&mut self, x: Rc<SpecifierClass>);
}
impl SpecifierStackExt for SpecifierStack {
    fn stdio_usage_status(&self) -> StdioUsageStatus {
        use self::StdioUsageStatus::*;

        if !self.addrtype.is_stdio() {
            return None;
        }

        let mut sus: StdioUsageStatus = IsItself;

        for overlay in self.overlays.iter().rev() {
            if overlay.is_reuser() {
                sus = WithReuser;
            } else if sus == IsItself {
                sus = Indirectly;
            }
        }
        sus
    }
    fn reuser_count(&self) -> usize {
        let mut c = 0;
        for overlay in &self.overlays {
            if overlay.is_reuser() {
                c += 1;
            }
        }
        c
    }
    fn contains(&self, t: &'static str) -> bool {
        for overlay in &self.overlays {
            if overlay.get_name() == t {
                return true;
            }
        }
        self.addrtype.get_name() == t
    }
    fn is_multiconnect(&self) -> bool {
        use super::ClassMulticonnectStatus::*;
        match self.addrtype.multiconnect_status() {
            MultiConnect => (),
            SingleConnect => return false,
            MulticonnectnessDependsOnInnerType => unreachable!(),
        }
        for overlay in self.overlays.iter().rev() {
            match overlay.multiconnect_status() {
                MultiConnect => (),
                SingleConnect => return false,
                MulticonnectnessDependsOnInnerType => (),
            }
        }
        true
    }
    fn is_stream_oriented(&self) -> bool {
        use super::ClassMessageBoundaryStatus::*;
        let mut q = match self.addrtype.message_boundary_status() {
            StreamOriented => true,
            MessageOriented => false,
            MessageBoundaryStatusDependsOnInnerType => unreachable!(),
        };
        for overlay in self.overlays.iter().rev() {
            match overlay.message_boundary_status() {
                StreamOriented => q = true,
                MessageOriented => q = false,
                MessageBoundaryStatusDependsOnInnerType => (),
            }
        }
        q
    }
    fn insert_line_class_in_proper_place(&mut self, x: Rc<SpecifierClass>) {
        use super::ClassMessageBoundaryStatus::*;
        let mut insert_idx = 0;
        for overlay in &self.overlays {
            match overlay.message_boundary_status() {
                StreamOriented => break,
                MessageOriented => break,
                MessageBoundaryStatusDependsOnInnerType => insert_idx += 1,
            }
        }
        self.overlays.insert(insert_idx, x);
    }
}

impl WebsocatConfiguration2 {
    pub fn inetd_mode(&self) -> bool {
        self.contains_class("InetdClass")
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
    pub fn websocket_used(&self) -> bool {
        false
        || self.contains_class("WsConnectClass")
        || self.contains_class("WsClientClass")
        || self.contains_class("WsClientSecureClass")
        || self.contains_class("WsServerClass")
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
    pub fn exec_used(&self) -> bool {
        false
        || self.contains_class("ExecClass")
        || self.contains_class("CmdClass")
        || self.contains_class("ShCClass")
    }

    pub fn contains_class(&self, x: &'static str) -> bool {
        self.s1.contains(x) || self.s2.contains(x)
    }

    pub fn get_exec_parameter(&self) -> Option<&str> {
        if self.s1.addrtype.get_name() == "ExecClass" {
            return Some(self.s1.addr.as_str());
        }
        if self.s2.addrtype.get_name() == "ExecClass" {
            return Some(self.s2.addr.as_str());
        }
        None
    }

    pub fn lint_and_fixup<F>(&mut self, on_warning: &F) -> super::Result<()>
    where
        F: for<'a> Fn(&'a str) -> () + 'static,
    {
        let mut reuser_has_been_inserted = false;
        let multiconnect = !self.opts.oneshot && self.s1.is_multiconnect();
        use self::StdioUsageStatus::{Indirectly, IsItself, None, WithReuser};
        match (self.s1.stdio_usage_status(), self.s2.stdio_usage_status()) {
            (_, None) => (),
            (None, WithReuser) => (),
            (None, IsItself) | (None, Indirectly) => {
                if multiconnect {
                    self.s2.overlays.insert(
                        0,
                        Rc::new(super::broadcast_reuse_peer::BroadcastReuserClass),
                    );
                    reuser_has_been_inserted = true;
                }
            }
            (IsItself, IsItself) => {
                info!("Special mode, expection from usual one-stdio rule. Acting like `cat(1)`");
                self.s2 = SpecifierStack::from_str("mirror:")?;
                if self.opts.unidirectional ^ self.opts.unidirectional_reverse {
                    self.opts.unidirectional = false;
                    self.opts.unidirectional_reverse = false;
                }
                return Ok(());
            }
            (_, _) => {
                Err("Too many usages of stdin/stdout. Specify it either on left or right address, not on both.")?;
            }
        }

        if self.s1.reuser_count() + self.s2.reuser_count() > 1 {
            if reuser_has_been_inserted {
                error!("The reuser you specified conflicts with automatically inserted reuser based on usage of stdin/stdout in multiconnect mode.");
            }
            Err("Too many usages of connection reuser. Please limit to only one instance.")?;
        }

        if !self.opts.no_auto_linemode && self.opts.websocket_text_mode {
            match (self.s1.is_stream_oriented(), self.s2.is_stream_oriented()) {
                (false, false) => {}
                (true, true) => {}
                (true, false) => {
                    info!("Auto-inserting the line mode");
                    self.s1.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Line2MessageClass,
                    ));
                    self.s2.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Message2LineClass,
                    ));
                }
                (false, true) => {
                    info!("Auto-inserting the line mode");
                    self.s2.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Line2MessageClass,
                    ));
                    self.s1.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Message2LineClass,
                    ));
                }
            }
        }

        if !self.opts.oneshot && self.s2.is_multiconnect() && !self.s1.is_multiconnect() {
            on_warning("You have specified a listener on the right (as the second positional argument) instead of on the left. It will only serve one connection.\nChange arguments order to enable multiple parallel connections or use --oneshot argument to make single connection explicit.");
        }

        if multiconnect
            && (self.s2.addrtype.get_name() == "WriteFileClass"
                || self.s2.addrtype.get_name() == "AppendFileClass")
            && self.s2.reuser_count() == 0
        {
            info!("Auto-inserting the reuser");
            self.s2
                .overlays
                .push(Rc::new(super::primitive_reuse_peer::ReuserClass));
        }

        if self.s1.addrtype.get_name() == "ExecClass" && self.s2.addrtype.get_name() == "ExecClass"
        {
            Err("Can't use exec: more than one time. Replace one of them with sh-c: or cmd:.")?;
        }

        if let Some(x) = self.get_exec_parameter() {
            if self.opts.exec_args.is_empty() && x.contains(' ') {
                on_warning("Warning: you specified exec: without the corresponding --exec-args at the end of command line. Unlike in cmd: or sh-c:, spaces inside exec:'s direct parameter are interpreted as part of program name, not as separator.");
            }
        }

        if self.opts.restrict_uri.is_some() && !self.contains_class("WsServerClass") {
            on_warning("--restrict-uri is meaningless without a WebSocket server");
        }

        if !self.opts.serve_static_files.is_empty() && !self.contains_class("WsServerClass") {
            on_warning("--static-file (-F) is meaningless without a WebSocket server");
        }

        for sf in &self.opts.serve_static_files {
            if !sf.uri.starts_with('/') {
                on_warning(&format!("Static file's URI `{}` should begin with `/`?", sf.uri));
            }
            if !sf.file.exists() {
                on_warning(&format!("File {:?} does not exist", sf.file));
            }
            if !sf.content_type.contains('/') {
                on_warning(&format!("Content-Type `{}` lacks `/` character",
                    sf.content_type));
            }
        }

        if self.opts.exec_set_env {
            if !self.exec_used() {
                on_warning("-e (--set-environment) is meaningless without a exec: or sh-c: or cmd: address");
            }
            if !self.contains_class("TcpListenClass") && !self.contains_class("WsServerClass") {
                on_warning("-e (--set-environment) is currently meaningless without a websocket server and/or TCP listener");
            }
        }

        // TODO: UDP connect oneshot mode

        // TODO: tests for the linter
        Ok(())
    }
}
<commit_msg>Add warning about the bug #13<commit_after>use super::{SpecifierClass, SpecifierStack, WebsocatConfiguration2};
use std::rc::Rc;
use std::str::FromStr;

#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
pub enum StdioUsageStatus {
    /// Does not use standard input or output at all
    None,
    /// Uses a reuser for connecting multiple peers at stdio, not distinguishing between IsItself and Indirectly
    WithReuser,
    /// Stdio wrapped into something (but not the reuser)
    Indirectly,
    /// Is the `-` or `stdio:` or `threadedstdio:` itself.
    IsItself,
}

trait ClassExt {
    fn is_stdio(&self) -> bool;
    fn is_reuser(&self) -> bool;
}

#[cfg_attr(rustfmt, rustfmt_skip)]
impl ClassExt for Rc<SpecifierClass> {
    fn is_stdio(&self) -> bool {
        [
            "StdioClass",
            "ThreadedStdioClass",
            "ThreadedStdioSubstituteClass",
        ].contains(&self.get_name())
    }
    fn is_reuser(&self) -> bool {
        [
            "ReuserClass",
            "BroadcastReuserClass",
        ].contains(&self.get_name())
    }
}

pub trait SpecifierStackExt {
    fn stdio_usage_status(&self) -> StdioUsageStatus;
    fn reuser_count(&self) -> usize;
    fn contains(&self, t: &'static str) -> bool;
    fn is_multiconnect(&self) -> bool;
    fn is_stream_oriented(&self) -> bool;
    fn insert_line_class_in_proper_place(&mut self, x: Rc<SpecifierClass>);
}
impl SpecifierStackExt for SpecifierStack {
    fn stdio_usage_status(&self) -> StdioUsageStatus {
        use self::StdioUsageStatus::*;

        if !self.addrtype.is_stdio() {
            return None;
        }

        let mut sus: StdioUsageStatus = IsItself;

        for overlay in self.overlays.iter().rev() {
            if overlay.is_reuser() {
                sus = WithReuser;
            } else if sus == IsItself {
                sus = Indirectly;
            }
        }
        sus
    }
    fn reuser_count(&self) -> usize {
        let mut c = 0;
        for overlay in &self.overlays {
            if overlay.is_reuser() {
                c += 1;
            }
        }
        c
    }
    fn contains(&self, t: &'static str) -> bool {
        for overlay in &self.overlays {
            if overlay.get_name() == t {
                return true;
            }
        }
        self.addrtype.get_name() == t
    }
    fn is_multiconnect(&self) -> bool {
        use super::ClassMulticonnectStatus::*;
        match self.addrtype.multiconnect_status() {
            MultiConnect => (),
            SingleConnect => return false,
            MulticonnectnessDependsOnInnerType => unreachable!(),
        }
        for overlay in self.overlays.iter().rev() {
            match overlay.multiconnect_status() {
                MultiConnect => (),
                SingleConnect => return false,
                MulticonnectnessDependsOnInnerType => (),
            }
        }
        true
    }
    fn is_stream_oriented(&self) -> bool {
        use super::ClassMessageBoundaryStatus::*;
        let mut q = match self.addrtype.message_boundary_status() {
            StreamOriented => true,
            MessageOriented => false,
            MessageBoundaryStatusDependsOnInnerType => unreachable!(),
        };
        for overlay in self.overlays.iter().rev() {
            match overlay.message_boundary_status() {
                StreamOriented => q = true,
                MessageOriented => q = false,
                MessageBoundaryStatusDependsOnInnerType => (),
            }
        }
        q
    }
    fn insert_line_class_in_proper_place(&mut self, x: Rc<SpecifierClass>) {
        use super::ClassMessageBoundaryStatus::*;
        let mut insert_idx = 0;
        for overlay in &self.overlays {
            match overlay.message_boundary_status() {
                StreamOriented => break,
                MessageOriented => break,
                MessageBoundaryStatusDependsOnInnerType => insert_idx += 1,
            }
        }
        self.overlays.insert(insert_idx, x);
    }
}

impl WebsocatConfiguration2 {
    pub fn inetd_mode(&self) -> bool {
        self.contains_class("InetdClass")
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
    pub fn websocket_used(&self) -> bool {
        false
        || self.contains_class("WsConnectClass")
        || self.contains_class("WsClientClass")
        || self.contains_class("WsClientSecureClass")
        || self.contains_class("WsServerClass")
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    #[cfg_attr(feature="cargo-clippy", allow(nonminimal_bool))]
    pub fn exec_used(&self) -> bool {
        false
        || self.contains_class("ExecClass")
        || self.contains_class("CmdClass")
        || self.contains_class("ShCClass")
    }

    pub fn contains_class(&self, x: &'static str) -> bool {
        self.s1.contains(x) || self.s2.contains(x)
    }

    pub fn get_exec_parameter(&self) -> Option<&str> {
        if self.s1.addrtype.get_name() == "ExecClass" {
            return Some(self.s1.addr.as_str());
        }
        if self.s2.addrtype.get_name() == "ExecClass" {
            return Some(self.s2.addr.as_str());
        }
        None
    }

    pub fn lint_and_fixup<F>(&mut self, on_warning: &F) -> super::Result<()>
    where
        F: for<'a> Fn(&'a str) -> () + 'static,
    {
        let mut reuser_has_been_inserted = false;
        let multiconnect = !self.opts.oneshot && self.s1.is_multiconnect();
        use self::StdioUsageStatus::{Indirectly, IsItself, None, WithReuser};
        match (self.s1.stdio_usage_status(), self.s2.stdio_usage_status()) {
            (_, None) => (),
            (None, WithReuser) => (),
            (None, IsItself) | (None, Indirectly) => {
                if multiconnect {
                    self.s2.overlays.insert(
                        0,
                        Rc::new(super::broadcast_reuse_peer::BroadcastReuserClass),
                    );
                    reuser_has_been_inserted = true;
                }
            }
            (IsItself, IsItself) => {
                info!("Special mode, expection from usual one-stdio rule. Acting like `cat(1)`");
                self.s2 = SpecifierStack::from_str("mirror:")?;
                if self.opts.unidirectional ^ self.opts.unidirectional_reverse {
                    self.opts.unidirectional = false;
                    self.opts.unidirectional_reverse = false;
                }
                return Ok(());
            }
            (_, _) => {
                Err("Too many usages of stdin/stdout. Specify it either on left or right address, not on both.")?;
            }
        }

        if self.s1.reuser_count() + self.s2.reuser_count() > 1 {
            if reuser_has_been_inserted {
                error!("The reuser you specified conflicts with automatically inserted reuser based on usage of stdin/stdout in multiconnect mode.");
            }
            Err("Too many usages of connection reuser. Please limit to only one instance.")?;
        }

        if !self.opts.no_auto_linemode && self.opts.websocket_text_mode {
            match (self.s1.is_stream_oriented(), self.s2.is_stream_oriented()) {
                (false, false) => {}
                (true, true) => {}
                (true, false) => {
                    info!("Auto-inserting the line mode");
                    self.s1.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Line2MessageClass,
                    ));
                    self.s2.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Message2LineClass,
                    ));
                }
                (false, true) => {
                    info!("Auto-inserting the line mode");
                    self.s2.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Line2MessageClass,
                    ));
                    self.s1.insert_line_class_in_proper_place(Rc::new(
                        super::line_peer::Message2LineClass,
                    ));
                }
            }
        }

        if !self.opts.oneshot && self.s2.is_multiconnect() && !self.s1.is_multiconnect() {
            on_warning("You have specified a listener on the right (as the second positional argument) instead of on the left. It will only serve one connection.\nChange arguments order to enable multiple parallel connections or use --oneshot argument to make single connection explicit.");
        }

        if multiconnect
            && (self.s2.addrtype.get_name() == "WriteFileClass"
                || self.s2.addrtype.get_name() == "AppendFileClass")
            && self.s2.reuser_count() == 0
        {
            info!("Auto-inserting the reuser");
            self.s2
                .overlays
                .push(Rc::new(super::primitive_reuse_peer::ReuserClass));
        }

        if self.s1.addrtype.get_name() == "ExecClass" && self.s2.addrtype.get_name() == "ExecClass"
        {
            Err("Can't use exec: more than one time. Replace one of them with sh-c: or cmd:.")?;
        }

        if let Some(x) = self.get_exec_parameter() {
            if self.opts.exec_args.is_empty() && x.contains(' ') {
                on_warning("Warning: you specified exec: without the corresponding --exec-args at the end of command line. Unlike in cmd: or sh-c:, spaces inside exec:'s direct parameter are interpreted as part of program name, not as separator.");
            }
        }

        if self.opts.restrict_uri.is_some() && !self.contains_class("WsServerClass") {
            on_warning("--restrict-uri is meaningless without a WebSocket server");
        }

        if !self.opts.serve_static_files.is_empty() && !self.contains_class("WsServerClass") {
            on_warning("--static-file (-F) is meaningless without a WebSocket server");
        }

        for sf in &self.opts.serve_static_files {
            if !sf.uri.starts_with('/') {
                on_warning(&format!("Static file's URI `{}` should begin with `/`?", sf.uri));
            }
            if !sf.file.exists() {
                on_warning(&format!("File {:?} does not exist", sf.file));
            }
            if !sf.content_type.contains('/') {
                on_warning(&format!("Content-Type `{}` lacks `/` character",
                    sf.content_type));
            }
        }

        if self.opts.exec_set_env {
            if !self.exec_used() {
                on_warning("-e (--set-environment) is meaningless without a exec: or sh-c: or cmd: address");
            }
            if !self.contains_class("TcpListenClass") && !self.contains_class("WsServerClass") {
                on_warning("-e (--set-environment) is currently meaningless without a websocket server and/or TCP listener");
            }
        }

        if !self.opts.oneshot && self.s1.is_multiconnect() {
            if self.s1.contains("TcpListenClass") || self.s1.contains("UnixListenClass") || self.s1.contains("SeqpacketListenClass") {
                if !self.opts.unidirectional && (self.opts.unidirectional_reverse || !self.opts.exit_on_eof) {
                    on_warning("Unfortunately, serving multiple clients without --exit-on-eof (-E) or with -U option is prone to socket leak in this websocat version");
                }
            }
        }

        // TODO: UDP connect oneshot mode

        // TODO: tests for the linter
        Ok(())
    }
}
<|endoftext|>