<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
use std::ptr;
use std::io;
use time::{Duration, SteadyTime};

use kernel32;
use winapi;

use error::{Error, Result};

use super::{HabExitStatus, ExitStatusExt, ShutdownMethod};

const STILL_ACTIVE: u32 = 259;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 {
    unsafe { kernel32::GetCurrentProcessId() as u32 }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: u32) -> bool {
    match handle_from_pid(pid) {
        Some(handle) => {
            unsafe {
                let _ = kernel32::CloseHandle(handle);
            }
            true
        }
        None => false,
    }
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn handle_from_pid(pid: u32) -> Option<winapi::HANDLE> {
    unsafe {
        let proc_handle = kernel32::OpenProcess(winapi::PROCESS_QUERY_LIMITED_INFORMATION |
                                                winapi::PROCESS_TERMINATE,
                                                winapi::FALSE,
                                                pid as winapi::DWORD);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

pub struct Child {
    handle: Option<winapi::HANDLE>,
    last_status: Option<u32>,
    pid: u32,
}

impl Child {
    // On windows we need the process handle to capture status
    // Here we will attempt to get the handle from the pid but if the
    // process dies before we can get it, we will just wait() on the
    // std::process::Child and cache the exit_status which we will return
    // when status is called.
    pub fn new(child: &mut process::Child) -> Result<Child> {
        let (win_handle, status) = match handle_from_pid(child.id()) {
            Some(handle) => (Some(handle), Ok(None)),
            _ => {
                (None,
                 {
                     match child.wait() {
                         Ok(exit) => Ok(Some(exit.code().unwrap() as u32)),
                         Err(e) => {
                             Err(format!("Failed to retrieve exit code for pid {} : {}",
                                         child.id(),
                                         e))
                         }
                     }
                 })
            }
        };

        match status {
            Ok(status) => {
                Ok(Child {
                       handle: win_handle,
                       last_status: status,
                       pid: child.id(),
                   })
            }
            Err(e) => Err(Error::GetHabChildFailed(e)),
        }
    }

    pub fn id(&self) -> u32 {
        self.pid
    }

    pub fn status(&mut self) -> Result<HabExitStatus> {
        if self.last_status.is_some() {
            return Ok(HabExitStatus { status: Some(self.last_status.unwrap()) });
        }

        let mut exit_status: u32 = 0;

        unsafe {
            let ret = kernel32::GetExitCodeProcess(self.handle.unwrap(),
                                                   &mut exit_status as winapi::LPDWORD);
            if ret == 0 {
                return Err(Error::GetExitCodeProcessFailed(format!("Failed to retrieve Exit \
                                                                    Code for pid {}: {}",
                                                                   self.pid,
                                                                   io::Error::last_os_error())));
            }
        }

        if exit_status == STILL_ACTIVE {
            return Ok(HabExitStatus { status: None });
        };

        Ok(HabExitStatus { status: Some(exit_status) })
    }

    pub fn kill(&mut self) -> Result<ShutdownMethod> {
        if self.last_status.is_some() {
            return Ok(ShutdownMethod::AlreadyExited);
        }

        let mut ret;
        unsafe {
            // Turn off ctrl-C handling for current process
            ret = kernel32::SetConsoleCtrlHandler(None, winapi::TRUE);
            if ret == 0 {
                debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                       self.pid,
                       io::Error::last_os_error());
            }

            if ret != 0 {
                // Send a ctrl-C
                ret = kernel32::GenerateConsoleCtrlEvent(0, 0);
                if ret == 0 {
                    debug!("Failed to send ctrl-c to pid {}: {}",
                           self.pid,
                           io::Error::last_os_error());
                }
            }
        }

        let stop_time = SteadyTime::now() + Duration::seconds(8);

        let result;
        loop {
            if ret == 0 || SteadyTime::now() > stop_time {
                unsafe {
                    ret = kernel32::TerminateProcess(self.handle.unwrap(), 1);
                    if ret == 0 {
                        result = Err(Error::TerminateProcessFailed(format!("Failed to call \
                                                                       terminate pid {}: {}",
                                                                      self.pid,
                                                                      io::Error::last_os_error())));
                    } else {
                        result = Ok(ShutdownMethod::Killed);
                    }
                    break;
                }
            }

            match self.status() {
                Ok(status) => {
                    if !status.no_status() {
                        result = Ok(ShutdownMethod::GracefulTermination);
                        break;
                    }
                }
                _ => {}
            }
        }

        // turn Ctrl-C handling back on for current process
        ret = unsafe { kernel32::SetConsoleCtrlHandler(None, winapi::FALSE) };
        if ret == 0 {
            debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                   self.pid,
                   io::Error::last_os_error());
        }

        result
    }
}

// Have to implement these due to our HANDLE field
unsafe impl Send for Child {}
unsafe impl Sync for Child {}

impl Drop for Child {
    fn drop(&mut self) {
        match self.handle {
            None => {}
            Some(handle) => unsafe {
                let _ = kernel32::CloseHandle(handle);
            },
        }
    }
}

impl ExitStatusExt for HabExitStatus {
    fn code(&self) -> Option<u32> {
        self.status
    }

    fn signal(&self) -> Option<u32> {
        None
    }
}

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

    #[test]
    fn running_process_returns_no_exit_status() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();

        assert!(hab_child.status().unwrap().no_status())
    }

    #[test]
    fn successfully_run_process_exits_zero() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("$a='b'");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(0))
    }

    #[test]
    fn terminated_process_returns_non_zero_exit() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.kill();

        assert!(hab_child.status().unwrap().code() != Some(0))
    }

    #[test]
    fn process_that_exits_with_specific_code_has_same_exit_code() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("exit 5000");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(5000))
    }
}
<commit_msg>is_alive should check exit status of handle for STILL_ACTIVE<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
use std::ptr;
use std::io;
use time::{Duration, SteadyTime};

use kernel32;
use winapi;

use error::{Error, Result};

use super::{HabExitStatus, ExitStatusExt, ShutdownMethod};

const STILL_ACTIVE: u32 = 259;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 {
    unsafe { kernel32::GetCurrentProcessId() as u32 }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: u32) -> bool {
    match handle_from_pid(pid) {
        Some(handle) => {
            let exit_status = exit_status(handle).expect("Failed to get exit status");
            unsafe {
                let _ = kernel32::CloseHandle(handle);
            }
            exit_status == STILL_ACTIVE
        }
        None => false,
    }
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn handle_from_pid(pid: u32) -> Option<winapi::HANDLE> {
    unsafe {
        let proc_handle = kernel32::OpenProcess(winapi::PROCESS_QUERY_LIMITED_INFORMATION |
                                                winapi::PROCESS_TERMINATE,
                                                winapi::FALSE,
                                                pid as winapi::DWORD);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

fn exit_status(handle: winapi::HANDLE) -> Result<u32> {
    let mut exit_status: u32 = 0;

    unsafe {
        let ret = kernel32::GetExitCodeProcess(handle, &mut exit_status as winapi::LPDWORD);
        if ret == 0 {
            return Err(Error::GetExitCodeProcessFailed(format!("Failed to retrieve Exit Code: {}",
                                                               io::Error::last_os_error())));
        }
    }

    Ok(exit_status)
}

pub struct Child {
    handle: Option<winapi::HANDLE>,
    last_status: Option<u32>,
    pid: u32,
}

impl Child {
    // On windows we need the process handle to capture status
    // Here we will attempt to get the handle from the pid but if the
    // process dies before we can get it, we will just wait() on the
    // std::process::Child and cache the exit_status which we will return
    // when status is called.
    pub fn new(child: &mut process::Child) -> Result<Child> {
        let (win_handle, status) = match handle_from_pid(child.id()) {
            Some(handle) => (Some(handle), Ok(None)),
            _ => {
                (None,
                 {
                     match child.wait() {
                         Ok(exit) => Ok(Some(exit.code().unwrap() as u32)),
                         Err(e) => {
                             Err(format!("Failed to retrieve exit code for pid {} : {}",
                                         child.id(),
                                         e))
                         }
                     }
                 })
            }
        };

        match status {
            Ok(status) => {
                Ok(Child {
                       handle: win_handle,
                       last_status: status,
                       pid: child.id(),
                   })
            }
            Err(e) => Err(Error::GetHabChildFailed(e)),
        }
    }

    pub fn id(&self) -> u32 {
        self.pid
    }

    pub fn status(&mut self) -> Result<HabExitStatus> {
        if self.last_status.is_some() {
            return Ok(HabExitStatus { status: Some(self.last_status.unwrap()) });
        }

        let exit_status = exit_status(self.handle.unwrap())?;

        if exit_status == STILL_ACTIVE {
            return Ok(HabExitStatus { status: None });
        };

        Ok(HabExitStatus { status: Some(exit_status) })
    }

    pub fn kill(&mut self) -> Result<ShutdownMethod> {
        if self.last_status.is_some() {
            return Ok(ShutdownMethod::AlreadyExited);
        }

        let mut ret;
        unsafe {
            // Turn off ctrl-C handling for current process
            ret = kernel32::SetConsoleCtrlHandler(None, winapi::TRUE);
            if ret == 0 {
                debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                       self.pid,
                       io::Error::last_os_error());
            }

            if ret != 0 {
                // Send a ctrl-C
                ret = kernel32::GenerateConsoleCtrlEvent(0, 0);
                if ret == 0 {
                    debug!("Failed to send ctrl-c to pid {}: {}",
                           self.pid,
                           io::Error::last_os_error());
                }
            }
        }

        let stop_time = SteadyTime::now() + Duration::seconds(8);

        let result;
        loop {
            if ret == 0 || SteadyTime::now() > stop_time {
                unsafe {
                    ret = kernel32::TerminateProcess(self.handle.unwrap(), 1);
                    if ret == 0 {
                        result = Err(Error::TerminateProcessFailed(format!("Failed to call \
                                                                       terminate pid {}: {}",
                                                                      self.pid,
                                                                      io::Error::last_os_error())));
                    } else {
                        result = Ok(ShutdownMethod::Killed);
                    }
                    break;
                }
            }

            match self.status() {
                Ok(status) => {
                    if !status.no_status() {
                        result = Ok(ShutdownMethod::GracefulTermination);
                        break;
                    }
                }
                _ => {}
            }
        }

        // turn Ctrl-C handling back on for current process
        ret = unsafe { kernel32::SetConsoleCtrlHandler(None, winapi::FALSE) };
        if ret == 0 {
            debug!("Failed to call SetConsoleCtrlHandler on pid {}: {}",
                   self.pid,
                   io::Error::last_os_error());
        }

        result
    }
}

// Have to implement these due to our HANDLE field
unsafe impl Send for Child {}
unsafe impl Sync for Child {}

impl Drop for Child {
    fn drop(&mut self) {
        match self.handle {
            None => {}
            Some(handle) => unsafe {
                let _ = kernel32::CloseHandle(handle);
            },
        }
    }
}

impl ExitStatusExt for HabExitStatus {
    fn code(&self) -> Option<u32> {
        self.status
    }

    fn signal(&self) -> Option<u32> {
        None
    }
}

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

    #[test]
    fn running_process_returns_no_exit_status() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();

        assert!(hab_child.status().unwrap().no_status())
    }

    #[test]
    fn successfully_run_process_exits_zero() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("$a='b'");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(0))
    }

    #[test]
    fn terminated_process_returns_non_zero_exit() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("while($true) { Start-Sleep 1 }");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.kill();

        assert!(hab_child.status().unwrap().code() != Some(0))
    }

    #[test]
    fn process_that_exits_with_specific_code_has_same_exit_code() {
        let mut cmd = Command::new("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.\
                                    exe");
        cmd.arg("-noprofile").arg("-command").arg("exit 5000");
        let mut child = cmd.spawn().unwrap();

        let mut hab_child = HabChild::from(&mut child).unwrap();
        let _ = child.wait();

        assert_eq!(hab_child.status().unwrap().code(), Some(5000))
    }
}
<|endoftext|>


<commit_before>use std;
use std::cmp;
use std::ptr;
use std::sync;

use io;

const KB_SIZE: usize = 1024;
pub type MemoryNode = sync::RwLock<[u8; KB_SIZE]>;

#[derive(Clone)]
pub enum MemoryBlock {
    Ram(sync::Arc<Vec<MemoryNode>>),
    Rom(sync::Arc<Vec<MemoryNode>>),
    Io(sync::Arc<(usize, sync::RwLock<io::IoRegion>)>),
}

impl MemoryBlock {
    pub fn make_ram(kbs: usize) -> MemoryBlock {
        let mut inner: Vec<MemoryNode> = Vec::new();
        for _ in 0..kbs {
            inner.push(sync::RwLock::new([0; KB_SIZE]))
        }

        MemoryBlock::Ram(sync::Arc::new(inner))
    }

    pub fn make_io(variant: io::IoRegion, kbs: usize) -> MemoryBlock {
        MemoryBlock::Io(sync::Arc::new((kbs, sync::RwLock::new(variant))))
    }

    pub fn get_bytes(&self) -> u32 {
        match *self {
            MemoryBlock::Ram(ref nodes) | MemoryBlock::Rom(ref nodes) => {
                (nodes.len() * KB_SIZE) as u32
            },
            MemoryBlock::Io(ref region) => (region.0 * KB_SIZE) as u32,
        }
    }

    pub unsafe fn read_to_ptr(&self, offset: usize, buf: *mut u8, buf_size: usize) {
        match *self {
            MemoryBlock::Ram(ref nodes) | MemoryBlock::Rom(ref nodes) => {
                let mut buf_remaining = buf_size;
                let mut node_index = offset / KB_SIZE;
                let mut node_pos = offset % KB_SIZE;

                while buf_remaining > 0 {
                    assert!(node_index < nodes.len());
                    let copy_amount = cmp::min(KB_SIZE - node_pos, buf_remaining);
                    {
                        let node_ptr = nodes[node_index].read().unwrap().as_ptr();
                        let buf_pos = buf_size - buf_remaining;
                        ptr::copy_nonoverlapping(node_ptr.offset(node_pos as isize), buf.offset(buf_pos as isize), copy_amount);
                    }
                    buf_remaining -= copy_amount;
                    node_index += 1;
                    node_pos = 0;
                }
            },
            MemoryBlock::Io(ref region) => match *region.1.read().unwrap() {
                io::IoRegion::Arm9(ref x) => x.read_reg(offset, buf, buf_size),
                _ => unimplemented!(),
            },
        }
    }

    pub unsafe fn write_from_ptr(&self, offset: usize, buf: *const u8, buf_size: usize) {
        match *self {
            MemoryBlock::Ram(ref nodes) => {
                let mut buf_remaining = buf_size;
                let mut node_index = offset / KB_SIZE;
                let mut node_pos = offset % KB_SIZE;

                while buf_remaining > 0 {
                    assert!(node_index < nodes.len());
                    let copy_amount = cmp::min(KB_SIZE - node_pos, buf_remaining);
                    {
                        let node_ptr = nodes[node_index].write().unwrap().as_mut_ptr();
                        let buf_pos = buf_size - buf_remaining;
                        ptr::copy_nonoverlapping(buf.offset(buf_pos as isize), node_ptr.offset(node_pos as isize), copy_amount);
                    }
                    buf_remaining -= copy_amount;
                    node_index += 1;
                    node_pos = 0;
                }
            },
            MemoryBlock::Rom(_) => panic!("Attempted to write to ROM!"),
            MemoryBlock::Io(ref region) => match *region.1.write().unwrap() {
                io::IoRegion::Arm9(ref mut x) => x.write_reg(offset, buf, buf_size),
                _ => unimplemented!(),
            },
        }
    }
}

pub struct MemController {
    regions: Vec<(u32, MemoryBlock)>,
}

impl MemController {
    pub fn new() -> MemController {
        MemController {
            regions: Vec::new(),
        }
    }

    fn search_region(&self, address: u32) -> Result<usize, usize> {
        self.regions.binary_search_by(|&(addr, _)| addr.cmp(&address))
    }

    fn match_address(&self, address: u32) -> Option<(u32, MemoryBlock)> {
        let index = match self.search_region(address) {
            Ok(a) => a,
            Err(a) => a - 1,
        };
        if index > self.regions.len() {
            return None;
        }

        let (block_addr, ref block) = self.regions[index];
        if (address >= block_addr) && (address < block_addr + block.get_bytes()) {
            return Some((block_addr, block.clone()));
        }
        None
    }

    pub fn map_region(&mut self, address: u32, region: MemoryBlock) {
        let insert_index = self.search_region(address).unwrap_err();
        self.regions.insert(insert_index, (address, region));
    }

    pub fn read<T: Copy>(&self, addr: u32) -> T {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            let t: T = std::mem::uninitialized();
            block.read_to_ptr((addr - block_addr) as usize, std::mem::transmute(&t), std::mem::size_of::<T>());
            t
        }
    }

    pub fn read_buf(&self, addr: u32, buf: &mut [u8]) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.read_to_ptr((addr - block_addr) as usize, buf.as_mut_ptr(), buf.len());
        }
    }

    pub fn write<T: Copy>(&mut self, addr: u32, data: T) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.write_from_ptr((addr - block_addr) as usize, std::mem::transmute(&data), std::mem::size_of::<T>());
        }
    }

    pub fn write_buf(&self, addr: u32, buf: &[u8]) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.write_from_ptr((addr - block_addr) as usize, buf.as_ptr(), buf.len());
        }
    }
}

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

    #[test]
    fn write_intra_block() {
        let block = MemoryBlock::new(1);
        assert_eq!(block.get_bytes(), 0x400);

        // Write data
        let bytes = [0xFFu8, 0x53u8, 0x28u8, 0xC6u8];
        unsafe {
            block.write_from_ptr(0x2C8, bytes.as_ptr(), bytes.len());
        }

        // Compare memory with data
        let block_mem = block.nodes[0].read().unwrap();
        assert_eq!(block_mem[0x2C8..0x2CC], bytes[..]);
    }

    #[test]
    fn read_intra_block() {
        let block = MemoryBlock::new(1);
        assert_eq!(block.get_bytes(), 0x400);

        // Write data directly to memory
        {
            let mut block_mem = block.nodes[0].write().unwrap();
            (&mut block_mem[0x2C8..0x2CC]).write_all(&[0xFFu8, 0x53u8, 0x28u8, 0xC6u8]);
        }

        // Read memory to buffer
        let mut buf = [0u8; 4];
        unsafe {
            block.read_to_ptr(0x2C8, buf.as_mut_ptr(), buf.len());
        }

        // Compare memory and buffer
        let block_mem = block.nodes[0].read().unwrap();
        assert_eq!(block_mem[0x2C8..0x2CC], buf[..]);
    }

    #[test]
    fn write_inter_block() {
        let block = MemoryBlock::new(2);
        assert_eq!(block.get_bytes(), 0x800);

        // Write data
        let bytes = [0xFFu8, 0x53u8, 0x28u8, 0xC6u8];
        unsafe {
            block.write_from_ptr(0x3FE, bytes.as_ptr(), bytes.len());
        }

        // Compare memory with data
        let block_mem0 = block.nodes[0].read().unwrap();
        let block_mem1 = block.nodes[1].read().unwrap();
        assert_eq!(block_mem0[0x3FE..0x400], bytes[0..2]);
        assert_eq!(block_mem1[0x0..0x2], bytes[2..4]);
    }

    #[test]
    fn read_inter_block() {
        let block = MemoryBlock::new(2);
        assert_eq!(block.get_bytes(), 0x800);

        // Write data directly to memory
        {
            let mut block_mem0 = block.nodes[0].write().unwrap();
            let mut block_mem1 = block.nodes[1].write().unwrap();
            (&mut block_mem0[0x3FE..0x400]).write_all(&[0xFFu8, 0x53u8]);
            (&mut block_mem1[0x0..0x2]).write_all(&[0x28u8, 0xC6u8]);
        }

        // Read memory to buffer
        let mut buf = [0u8; 4];
        unsafe {
            block.read_to_ptr(0x3FE, buf.as_mut_ptr(), buf.len());
        }

        // Compare memory and buffer
        let block_mem0 = block.nodes[0].read().unwrap();
        let block_mem1 = block.nodes[1].read().unwrap();
        assert_eq!(block_mem0[0x3FE..0x400], buf[0..2]);
        assert_eq!(block_mem1[0x0..0x2], buf[2..4]);
    }
}<commit_msg>mem: fix test breakage after MemoryBlock refactor<commit_after>use std;
use std::cmp;
use std::ptr;
use std::sync;

use io;

const KB_SIZE: usize = 1024;
pub type MemoryNode = sync::RwLock<[u8; KB_SIZE]>;

#[derive(Clone)]
pub enum MemoryBlock {
    Ram(sync::Arc<Vec<MemoryNode>>),
    Rom(sync::Arc<Vec<MemoryNode>>),
    Io(sync::Arc<(usize, sync::RwLock<io::IoRegion>)>),
}

impl MemoryBlock {
    pub fn make_ram(kbs: usize) -> MemoryBlock {
        let mut inner: Vec<MemoryNode> = Vec::new();
        for _ in 0..kbs {
            inner.push(sync::RwLock::new([0; KB_SIZE]))
        }

        MemoryBlock::Ram(sync::Arc::new(inner))
    }

    pub fn make_io(variant: io::IoRegion, kbs: usize) -> MemoryBlock {
        MemoryBlock::Io(sync::Arc::new((kbs, sync::RwLock::new(variant))))
    }

    pub fn get_bytes(&self) -> u32 {
        match *self {
            MemoryBlock::Ram(ref nodes) | MemoryBlock::Rom(ref nodes) => {
                (nodes.len() * KB_SIZE) as u32
            },
            MemoryBlock::Io(ref region) => (region.0 * KB_SIZE) as u32,
        }
    }

    pub unsafe fn read_to_ptr(&self, offset: usize, buf: *mut u8, buf_size: usize) {
        match *self {
            MemoryBlock::Ram(ref nodes) | MemoryBlock::Rom(ref nodes) => {
                let mut buf_remaining = buf_size;
                let mut node_index = offset / KB_SIZE;
                let mut node_pos = offset % KB_SIZE;

                while buf_remaining > 0 {
                    assert!(node_index < nodes.len());
                    let copy_amount = cmp::min(KB_SIZE - node_pos, buf_remaining);
                    {
                        let node_ptr = nodes[node_index].read().unwrap().as_ptr();
                        let buf_pos = buf_size - buf_remaining;
                        ptr::copy_nonoverlapping(node_ptr.offset(node_pos as isize), buf.offset(buf_pos as isize), copy_amount);
                    }
                    buf_remaining -= copy_amount;
                    node_index += 1;
                    node_pos = 0;
                }
            },
            MemoryBlock::Io(ref region) => match *region.1.read().unwrap() {
                io::IoRegion::Arm9(ref x) => x.read_reg(offset, buf, buf_size),
                _ => unimplemented!(),
            },
        }
    }

    pub unsafe fn write_from_ptr(&self, offset: usize, buf: *const u8, buf_size: usize) {
        match *self {
            MemoryBlock::Ram(ref nodes) => {
                let mut buf_remaining = buf_size;
                let mut node_index = offset / KB_SIZE;
                let mut node_pos = offset % KB_SIZE;

                while buf_remaining > 0 {
                    assert!(node_index < nodes.len());
                    let copy_amount = cmp::min(KB_SIZE - node_pos, buf_remaining);
                    {
                        let node_ptr = nodes[node_index].write().unwrap().as_mut_ptr();
                        let buf_pos = buf_size - buf_remaining;
                        ptr::copy_nonoverlapping(buf.offset(buf_pos as isize), node_ptr.offset(node_pos as isize), copy_amount);
                    }
                    buf_remaining -= copy_amount;
                    node_index += 1;
                    node_pos = 0;
                }
            },
            MemoryBlock::Rom(_) => panic!("Attempted to write to ROM!"),
            MemoryBlock::Io(ref region) => match *region.1.write().unwrap() {
                io::IoRegion::Arm9(ref mut x) => x.write_reg(offset, buf, buf_size),
                _ => unimplemented!(),
            },
        }
    }
}

pub struct MemController {
    regions: Vec<(u32, MemoryBlock)>,
}

impl MemController {
    pub fn new() -> MemController {
        MemController {
            regions: Vec::new(),
        }
    }

    fn search_region(&self, address: u32) -> Result<usize, usize> {
        self.regions.binary_search_by(|&(addr, _)| addr.cmp(&address))
    }

    fn match_address(&self, address: u32) -> Option<(u32, MemoryBlock)> {
        let index = match self.search_region(address) {
            Ok(a) => a,
            Err(a) => a - 1,
        };
        if index > self.regions.len() {
            return None;
        }

        let (block_addr, ref block) = self.regions[index];
        if (address >= block_addr) && (address < block_addr + block.get_bytes()) {
            return Some((block_addr, block.clone()));
        }
        None
    }

    pub fn map_region(&mut self, address: u32, region: MemoryBlock) {
        let insert_index = self.search_region(address).unwrap_err();
        self.regions.insert(insert_index, (address, region));
    }

    pub fn read<T: Copy>(&self, addr: u32) -> T {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            let t: T = std::mem::uninitialized();
            block.read_to_ptr((addr - block_addr) as usize, std::mem::transmute(&t), std::mem::size_of::<T>());
            t
        }
    }

    pub fn read_buf(&self, addr: u32, buf: &mut [u8]) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.read_to_ptr((addr - block_addr) as usize, buf.as_mut_ptr(), buf.len());
        }
    }

    pub fn write<T: Copy>(&mut self, addr: u32, data: T) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.write_from_ptr((addr - block_addr) as usize, std::mem::transmute(&data), std::mem::size_of::<T>());
        }
    }

    pub fn write_buf(&self, addr: u32, buf: &[u8]) {
        let (block_addr, block) = self.match_address(addr)
            .unwrap_or_else(|| panic!("Could not match address 0x{:X}", addr));
        unsafe {
            block.write_from_ptr((addr - block_addr) as usize, buf.as_ptr(), buf.len());
        }
    }
}

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

    #[test]
    fn write_intra_block() {
        let block = MemoryBlock::make_ram(1);
        assert_eq!(block.get_bytes(), 0x400);
        let nodes = match block {
            MemoryBlock::Ram(ref n) => n,
            _ => panic!("make_ram() did not make a Ram block!"),
        };

        // Write data
        let bytes = [0xFFu8, 0x53u8, 0x28u8, 0xC6u8];
        unsafe {
            block.write_from_ptr(0x2C8, bytes.as_ptr(), bytes.len());
        }

        // Compare memory with data
        let block_mem = nodes[0].read().unwrap();
        assert_eq!(block_mem[0x2C8..0x2CC], bytes[..]);
    }

    #[test]
    fn read_intra_block() {
        let block = MemoryBlock::make_ram(1);
        assert_eq!(block.get_bytes(), 0x400);
        let nodes = match block {
            MemoryBlock::Ram(ref n) => n,
            _ => panic!("make_ram() did not make a Ram block!"),
        };

        // Write data directly to memory
        {
            let mut block_mem = nodes[0].write().unwrap();
            (&mut block_mem[0x2C8..0x2CC]).write_all(&[0xFFu8, 0x53u8, 0x28u8, 0xC6u8]);
        }

        // Read memory to buffer
        let mut buf = [0u8; 4];
        unsafe {
            block.read_to_ptr(0x2C8, buf.as_mut_ptr(), buf.len());
        }

        // Compare memory and buffer
        let block_mem = nodes[0].read().unwrap();
        assert_eq!(block_mem[0x2C8..0x2CC], buf[..]);
    }

    #[test]
    fn write_inter_block() {
        let block = MemoryBlock::make_ram(2);
        assert_eq!(block.get_bytes(), 0x800);
        let nodes = match block {
            MemoryBlock::Ram(ref n) => n,
            _ => panic!("make_ram() did not make a Ram block!"),
        };

        // Write data
        let bytes = [0xFFu8, 0x53u8, 0x28u8, 0xC6u8];
        unsafe {
            block.write_from_ptr(0x3FE, bytes.as_ptr(), bytes.len());
        }

        // Compare memory with data
        let block_mem0 = nodes[0].read().unwrap();
        let block_mem1 = nodes[1].read().unwrap();
        assert_eq!(block_mem0[0x3FE..0x400], bytes[0..2]);
        assert_eq!(block_mem1[0x0..0x2], bytes[2..4]);
    }

    #[test]
    fn read_inter_block() {
        let block = MemoryBlock::make_ram(2);
        assert_eq!(block.get_bytes(), 0x800);
        let nodes = match block {
            MemoryBlock::Ram(ref n) => n,
            _ => panic!("make_ram() did not make a Ram block!"),
        };

        // Write data directly to memory
        {
            let mut block_mem0 = nodes[0].write().unwrap();
            let mut block_mem1 = nodes[1].write().unwrap();
            (&mut block_mem0[0x3FE..0x400]).write_all(&[0xFFu8, 0x53u8]);
            (&mut block_mem1[0x0..0x2]).write_all(&[0x28u8, 0xC6u8]);
        }

        // Read memory to buffer
        let mut buf = [0u8; 4];
        unsafe {
            block.read_to_ptr(0x3FE, buf.as_mut_ptr(), buf.len());
        }

        // Compare memory and buffer
        let block_mem0 = nodes[0].read().unwrap();
        let block_mem1 = nodes[1].read().unwrap();
        assert_eq!(block_mem0[0x3FE..0x400], buf[0..2]);
        assert_eq!(block_mem1[0x0..0x2], buf[2..4]);
    }
}<|endoftext|>


<commit_before>use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{Result, Write};

/// Header used by TGA image files
struct Header {
    id_length: u8, // length of ID string
    map_type: u8, // color map type
    image_type: u8, // image type code
    map_origin: u16, // starting index of map
    map_length: u16, // length of map
    map_entry_size: u8, // size of map entries in bits
    x_origin: u16, // x-origin of image
    y_origin: u16, // y-origin of image
    image_width: u16, // width of image
    image_height: u16, // height of image
    pixel_depth: u8, // bits per pixel
    image_desc: u8, // image descriptor
}

pub struct TgaColor {
    pub bgra: [u8; 4],
}

impl TgaColor {
    fn write<W: Write>(&self, w: &mut W) -> Result<usize> {
        w.write(&self.bgra)
    }
}

const RAW_TRUE_COLOR: u8 = 2;
const ARGB_DEPTH: u8 = 32;

impl Header {
    fn new(w: u16, h: u16) -> Header {
        Header {
            id_length: 0,
            map_type: 0,
            image_type: RAW_TRUE_COLOR,
            map_origin: 0,
            map_length: 0,
            map_entry_size: 0,
            x_origin: 0,
            y_origin: 0,
            image_width: w,
            image_height: h,
            pixel_depth: ARGB_DEPTH,
            image_desc: 0,
        }
    }
    fn write<W: WriteBytesExt>(&self, w: &mut W) -> Result<()> {
        try!(w.write_u8(self.id_length));
        try!(w.write_u8(self.map_type));
        try!(w.write_u8(self.image_type));
        try!(w.write_u16::<LittleEndian>(self.map_origin));
        try!(w.write_u16::<LittleEndian>(self.map_length));
        try!(w.write_u8(self.map_entry_size));
        try!(w.write_u16::<LittleEndian>(self.x_origin));
        try!(w.write_u16::<LittleEndian>(self.y_origin));
        try!(w.write_u16::<LittleEndian>(self.image_width));
        try!(w.write_u16::<LittleEndian>(self.image_height));
        try!(w.write_u8(self.pixel_depth));
        try!(w.write_u8(self.image_desc));
        Ok(())
    }
}

pub struct TgaImage<'a> {
    pub width: usize,
    pub height: usize,
    data: Vec<&'a TgaColor>,
}

impl<'a> TgaImage<'a> {
    pub fn new<'b>(w: usize, h: usize, c: &'b TgaColor) -> TgaImage<'b> {
        TgaImage {
            width: w,
            height: h,
            data: vec![c; w * h],
        }
    }
    pub fn write<W: Write>(&self, w: &mut W) -> Result<()> {
        try!(Header::new(self.width as u16, self.height as u16).write(w));
        for c in &self.data {
            try!(c.write(w));
        }
        Ok(())
    }
    pub fn set(&mut self, i: usize, j: usize, t: &'a TgaColor) -> () {
        if i < self.height && j < self.width {
            self.data[j * self.height + i] = t
        }
    }
}
<commit_msg>draft<commit_after>use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{Result, Write};

/// Header used by TGA image files
struct Header {
    id_length: u8,
    map_type: u8,
    image_type: u8,
    map_origin: u16,
    map_length: u16,
    map_entry_size: u8,
    x_origin: u16,
    y_origin: u16,
    image_width: u16,
    image_height: u16,
    pixel_depth: u8,
    image_desc: u8,
}

pub struct TgaColor {
    pub bgra: [u8; 4],
}

impl TgaColor {
    fn write<W: Write>(&self, w: &mut W) -> Result<usize> {
        w.write(&self.bgra)
    }
}

const RAW_TRUE_COLOR: u8 = 2;
const ARGB_DEPTH: u8 = 32;

impl Header {
    fn new(w: u16, h: u16) -> Header {
        Header {
            id_length: 0,
            map_type: 0,
            image_type: RAW_TRUE_COLOR,
            map_origin: 0,
            map_length: 0,
            map_entry_size: 0,
            x_origin: 0,
            y_origin: 0,
            image_width: w,
            image_height: h,
            pixel_depth: ARGB_DEPTH,
            image_desc: 0,
        }
    }
    fn write<W: WriteBytesExt>(&self, w: &mut W) -> Result<()> {
        try!(w.write_u8(self.id_length));
        try!(w.write_u8(self.map_type));
        try!(w.write_u8(self.image_type));
        try!(w.write_u16::<LittleEndian>(self.map_origin));
        try!(w.write_u16::<LittleEndian>(self.map_length));
        try!(w.write_u8(self.map_entry_size));
        try!(w.write_u16::<LittleEndian>(self.x_origin));
        try!(w.write_u16::<LittleEndian>(self.y_origin));
        try!(w.write_u16::<LittleEndian>(self.image_width));
        try!(w.write_u16::<LittleEndian>(self.image_height));
        try!(w.write_u8(self.pixel_depth));
        try!(w.write_u8(self.image_desc));
        Ok(())
    }
}

pub struct TgaImage<'a> {
    pub width: usize,
    pub height: usize,
    data: Vec<&'a TgaColor>,
}

impl<'a> TgaImage<'a> {
    pub fn new<'b>(w: usize, h: usize, c: &'b TgaColor) -> TgaImage<'b> {
        TgaImage {
            width: w,
            height: h,
            data: vec![c; w * h],
        }
    }
    pub fn write<W: Write>(&self, w: &mut W) -> Result<()> {
        try!(Header::new(self.width as u16, self.height as u16).write(w));
        for c in &self.data {
            try!(c.write(w));
        }
        Ok(())
    }
    pub fn set(&mut self, i: usize, j: usize, t: &'a TgaColor) -> () {
        if i < self.height && j < self.width {
            self.data[j * self.height + i] = t
        }
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use crate::{AvroResult, Error};
use num_bigint::{BigInt, Sign};

#[derive(Debug, Clone)]
pub struct Decimal {
    value: BigInt,
    len: usize,
}

// We only care about value equality, not byte length. Can two equal `BigInt`s have two different
// byte lengths?
impl PartialEq for Decimal {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

impl Decimal {
    pub(crate) fn len(&self) -> usize {
        self.len
    }

    fn to_vec(&self) -> AvroResult<Vec<u8>> {
        self.to_sign_extended_bytes_with_len(self.len)
    }

    pub(crate) fn to_sign_extended_bytes_with_len(&self, len: usize) -> AvroResult<Vec<u8>> {
        let sign_byte = 0xFF * u8::from(self.value.sign() == Sign::Minus);
        let mut decimal_bytes = vec![sign_byte; len];
        let raw_bytes = self.value.to_signed_bytes_be();
        let num_raw_bytes = raw_bytes.len();
        let start_byte_index = len.checked_sub(num_raw_bytes).ok_or(Error::SignExtend {
            requested: len,
            needed: num_raw_bytes,
        })?;
        decimal_bytes[start_byte_index..].copy_from_slice(&raw_bytes);
        Ok(decimal_bytes)
    }
}

impl std::convert::TryFrom<&Decimal> for Vec<u8> {
    type Error = Error;

    fn try_from(decimal: &Decimal) -> Result<Self, Self::Error> {
        decimal.to_vec()
    }
}

impl<T: AsRef<[u8]>> From<T> for Decimal {
    fn from(bytes: T) -> Self {
        let bytes_ref = bytes.as_ref();
        Self {
            value: BigInt::from_signed_bytes_be(bytes_ref),
            len: bytes_ref.len(),
        }
    }
}
<commit_msg>AVRO-3331: Add TryFrom owned Decimal (#1502)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use crate::{AvroResult, Error};
use num_bigint::{BigInt, Sign};

#[derive(Debug, Clone)]
pub struct Decimal {
    value: BigInt,
    len: usize,
}

// We only care about value equality, not byte length. Can two equal `BigInt`s have two different
// byte lengths?
impl PartialEq for Decimal {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

impl Decimal {
    pub(crate) fn len(&self) -> usize {
        self.len
    }

    fn to_vec(&self) -> AvroResult<Vec<u8>> {
        self.to_sign_extended_bytes_with_len(self.len)
    }

    pub(crate) fn to_sign_extended_bytes_with_len(&self, len: usize) -> AvroResult<Vec<u8>> {
        let sign_byte = 0xFF * u8::from(self.value.sign() == Sign::Minus);
        let mut decimal_bytes = vec![sign_byte; len];
        let raw_bytes = self.value.to_signed_bytes_be();
        let num_raw_bytes = raw_bytes.len();
        let start_byte_index = len.checked_sub(num_raw_bytes).ok_or(Error::SignExtend {
            requested: len,
            needed: num_raw_bytes,
        })?;
        decimal_bytes[start_byte_index..].copy_from_slice(&raw_bytes);
        Ok(decimal_bytes)
    }
}

/// Gets the internal byte array representation of a referenced decimal.
/// Usage:
/// ```
/// use apache_avro::Decimal;
/// use std::convert::TryFrom;
///
/// let decimal = Decimal::from(vec![1, 24]);
/// let maybe_bytes = <Vec<u8>>::try_from(&decimal);
/// ```
impl std::convert::TryFrom<&Decimal> for Vec<u8> {
    type Error = Error;

    fn try_from(decimal: &Decimal) -> Result<Self, Self::Error> {
        decimal.to_vec()
    }
}

/// Gets the internal byte array representation of an owned decimal.
/// Usage:
/// ```
/// use apache_avro::Decimal;
/// use std::convert::TryFrom;
///
/// let decimal = Decimal::from(vec![1, 24]);
/// let maybe_bytes = <Vec<u8>>::try_from(decimal);
/// ```
impl std::convert::TryFrom<Decimal> for Vec<u8> {
    type Error = Error;

    fn try_from(decimal: Decimal) -> Result<Self, Self::Error> {
        decimal.to_vec()
    }
}

impl<T: AsRef<[u8]>> From<T> for Decimal {
    fn from(bytes: T) -> Self {
        let bytes_ref = bytes.as_ref();
        Self {
            value: BigInt::from_signed_bytes_be(bytes_ref),
            len: bytes_ref.len(),
        }
    }
}

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

    #[test]
    fn test_decimal_from_bytes_from_ref_decimal() {
        let input = vec![1, 24];
        let d = Decimal::from(&input);

        let output = <Vec<u8>>::try_from(&d).unwrap();
        assert_eq!(output, input);
    }

    #[test]
    fn test_decimal_from_bytes_from_owned_decimal() {
        let input = vec![1, 24];
        let d = Decimal::from(&input);

        let output = <Vec<u8>>::try_from(d).unwrap();
        assert_eq!(output, input);
    }
}
<|endoftext|>


<commit_before>// arch/powerpc/include/uapi/asm/socket.h

pub const SOL_SOCKET: ::c_int = 1;

// Defined in unix/linux_like/mod.rs
// pub const SO_DEBUG: ::c_int = 1;
pub const SO_REUSEADDR: ::c_int = 2;
pub const SO_TYPE: ::c_int = 3;
pub const SO_ERROR: ::c_int = 4;
pub const SO_DONTROUTE: ::c_int = 5;
pub const SO_BROADCAST: ::c_int = 6;
pub const SO_SNDBUF: ::c_int = 7;
pub const SO_RCVBUF: ::c_int = 8;
pub const SO_KEEPALIVE: ::c_int = 9;
pub const SO_OOBINLINE: ::c_int = 10;
pub const SO_NO_CHECK: ::c_int = 11;
pub const SO_PRIORITY: ::c_int = 12;
pub const SO_LINGER: ::c_int = 13;
pub const SO_BSDCOMPAT: ::c_int = 14;
pub const SO_REUSEPORT: ::c_int = 15;
// powerpc only differs in these
pub const SO_RCVLOWAT: ::c_int = 16;
pub const SO_SNDLOWAT: ::c_int = 17;
pub const SO_RCVTIMEO: ::c_int = 18;
pub const SO_SNDTIMEO: ::c_int = 19;
// pub const SO_RCVTIMEO_OLD: ::c_int = 18;
// pub const SO_SNDTIMEO_OLD: ::c_int = 19;
pub const SO_PASSCRED: ::c_int = 20;
pub const SO_PEERCRED: ::c_int = 21;
// end
pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
pub const SO_BINDTODEVICE: ::c_int = 25;
pub const SO_ATTACH_FILTER: ::c_int = 26;
pub const SO_DETACH_FILTER: ::c_int = 27;
pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
pub const SO_PEERNAME: ::c_int = 28;
pub const SO_TIMESTAMP: ::c_int = 29;
// pub const SO_TIMESTAMP_OLD: ::c_int = 29;
pub const SO_ACCEPTCONN: ::c_int = 30;
pub const SO_PEERSEC: ::c_int = 31;
pub const SO_SNDBUFFORCE: ::c_int = 32;
pub const SO_RCVBUFFORCE: ::c_int = 33;
pub const SO_PASSSEC: ::c_int = 34;
pub const SO_TIMESTAMPNS: ::c_int = 35;
// pub const SO_TIMESTAMPNS_OLD: ::c_int = 35;
pub const SO_MARK: ::c_int = 36;
pub const SO_TIMESTAMPING: ::c_int = 37;
// pub const SO_TIMESTAMPING_OLD: ::c_int = 37;
pub const SO_PROTOCOL: ::c_int = 38;
pub const SO_DOMAIN: ::c_int = 39;
pub const SO_RXQ_OVFL: ::c_int = 40;
pub const SO_WIFI_STATUS: ::c_int = 41;
pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
pub const SO_PEEK_OFF: ::c_int = 42;
pub const SO_NOFCS: ::c_int = 43;
pub const SO_LOCK_FILTER: ::c_int = 44;
pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
pub const SO_BUSY_POLL: ::c_int = 46;
pub const SO_MAX_PACING_RATE: ::c_int = 47;
pub const SO_BPF_EXTENSIONS: ::c_int = 48;
pub const SO_INCOMING_CPU: ::c_int = 49;
pub const SO_ATTACH_BPF: ::c_int = 50;
pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
pub const SO_ATTACH_REUSEPORT_CBPF: ::c_int = 51;
pub const SO_ATTACH_REUSEPORT_EBPF: ::c_int = 52;
pub const SO_CNX_ADVICE: ::c_int = 53;
pub const SCM_TIMESTAMPING_OPT_STATS: ::c_int = 54;
pub const SO_MEMINFO: ::c_int = 55;
pub const SO_INCOMING_NAPI_ID: ::c_int = 56;
pub const SO_COOKIE: ::c_int = 57;
pub const SCM_TIMESTAMPING_PKTINFO: ::c_int = 58;
pub const SO_PEERGROUPS: ::c_int = 59;
pub const SO_ZEROCOPY: ::c_int = 60;
pub const SO_TXTIME: ::c_int = 61;
pub const SCM_TXTIME: ::c_int = SO_TXTIME;
pub const SO_BINDTOIFINDEX: ::c_int = 62;
// pub const SO_TIMESTAMP_NEW: ::c_int = 63;
// pub const SO_TIMESTAMPNS_NEW: ::c_int = 64;
// pub const SO_TIMESTAMPING_NEW: ::c_int = 65;
// pub const SO_RCVTIMEO_NEW: ::c_int = 66;
// pub const SO_SNDTIMEO_NEW: ::c_int = 67;
// pub const SO_DETACH_REUSEPORT_BPF: ::c_int = 68;
// pub const SO_PREFER_BUSY_POLL: ::c_int = 69;
// pub const SO_BUSY_POLL_BUDGET: ::c_int = 70;

// Defined in unix/linux_like/mod.rs
// pub const SCM_TIMESTAMP: ::c_int = SO_TIMESTAMP;
pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;

pub const TIOCMGET: ::Ioctl = 0x5415;
pub const TIOCMBIS: ::Ioctl = 0x5416;
pub const TIOCMBIC: ::Ioctl = 0x5417;
pub const TIOCMSET: ::Ioctl = 0x5418;

pub const TIOCM_LE: ::c_int = 0x001;
pub const TIOCM_DTR: ::c_int = 0x002;
pub const TIOCM_RTS: ::c_int = 0x004;
pub const TIOCM_ST: ::c_int = 0x008;
pub const TIOCM_SR: ::c_int = 0x010;
pub const TIOCM_CTS: ::c_int = 0x020;
pub const TIOCM_CAR: ::c_int = 0x040;
pub const TIOCM_CD: ::c_int = TIOCM_CAR;
pub const TIOCM_RNG: ::c_int = 0x080;
pub const TIOCM_RI: ::c_int = TIOCM_RNG;
pub const TIOCM_DSR: ::c_int = 0x100;

pub const BOTHER: ::speed_t = 0o0037;
pub const IBSHIFT: ::tcflag_t = 16;

cfg_if! {
    if #[cfg(target_endian = "little")] {
        pub const BLKSSZGET: ::c_int = 0x1268;
        pub const BLKPBSZGET: ::c_int = 0x127B;
    } else if #[cfg(target_endian = "big")] {
        pub const BLKSSZGET: ::c_int = 0x6812;
        pub const BLKPBSZGET: ::c_int = 0x7B12;
    }
}
<commit_msg>Fix ioctl constants on PowerPC<commit_after>// arch/powerpc/include/uapi/asm/socket.h

pub const SOL_SOCKET: ::c_int = 1;

// Defined in unix/linux_like/mod.rs
// pub const SO_DEBUG: ::c_int = 1;
pub const SO_REUSEADDR: ::c_int = 2;
pub const SO_TYPE: ::c_int = 3;
pub const SO_ERROR: ::c_int = 4;
pub const SO_DONTROUTE: ::c_int = 5;
pub const SO_BROADCAST: ::c_int = 6;
pub const SO_SNDBUF: ::c_int = 7;
pub const SO_RCVBUF: ::c_int = 8;
pub const SO_KEEPALIVE: ::c_int = 9;
pub const SO_OOBINLINE: ::c_int = 10;
pub const SO_NO_CHECK: ::c_int = 11;
pub const SO_PRIORITY: ::c_int = 12;
pub const SO_LINGER: ::c_int = 13;
pub const SO_BSDCOMPAT: ::c_int = 14;
pub const SO_REUSEPORT: ::c_int = 15;
// powerpc only differs in these
pub const SO_RCVLOWAT: ::c_int = 16;
pub const SO_SNDLOWAT: ::c_int = 17;
pub const SO_RCVTIMEO: ::c_int = 18;
pub const SO_SNDTIMEO: ::c_int = 19;
// pub const SO_RCVTIMEO_OLD: ::c_int = 18;
// pub const SO_SNDTIMEO_OLD: ::c_int = 19;
pub const SO_PASSCRED: ::c_int = 20;
pub const SO_PEERCRED: ::c_int = 21;
// end
pub const SO_SECURITY_AUTHENTICATION: ::c_int = 22;
pub const SO_SECURITY_ENCRYPTION_TRANSPORT: ::c_int = 23;
pub const SO_SECURITY_ENCRYPTION_NETWORK: ::c_int = 24;
pub const SO_BINDTODEVICE: ::c_int = 25;
pub const SO_ATTACH_FILTER: ::c_int = 26;
pub const SO_DETACH_FILTER: ::c_int = 27;
pub const SO_GET_FILTER: ::c_int = SO_ATTACH_FILTER;
pub const SO_PEERNAME: ::c_int = 28;
pub const SO_TIMESTAMP: ::c_int = 29;
// pub const SO_TIMESTAMP_OLD: ::c_int = 29;
pub const SO_ACCEPTCONN: ::c_int = 30;
pub const SO_PEERSEC: ::c_int = 31;
pub const SO_SNDBUFFORCE: ::c_int = 32;
pub const SO_RCVBUFFORCE: ::c_int = 33;
pub const SO_PASSSEC: ::c_int = 34;
pub const SO_TIMESTAMPNS: ::c_int = 35;
// pub const SO_TIMESTAMPNS_OLD: ::c_int = 35;
pub const SO_MARK: ::c_int = 36;
pub const SO_TIMESTAMPING: ::c_int = 37;
// pub const SO_TIMESTAMPING_OLD: ::c_int = 37;
pub const SO_PROTOCOL: ::c_int = 38;
pub const SO_DOMAIN: ::c_int = 39;
pub const SO_RXQ_OVFL: ::c_int = 40;
pub const SO_WIFI_STATUS: ::c_int = 41;
pub const SCM_WIFI_STATUS: ::c_int = SO_WIFI_STATUS;
pub const SO_PEEK_OFF: ::c_int = 42;
pub const SO_NOFCS: ::c_int = 43;
pub const SO_LOCK_FILTER: ::c_int = 44;
pub const SO_SELECT_ERR_QUEUE: ::c_int = 45;
pub const SO_BUSY_POLL: ::c_int = 46;
pub const SO_MAX_PACING_RATE: ::c_int = 47;
pub const SO_BPF_EXTENSIONS: ::c_int = 48;
pub const SO_INCOMING_CPU: ::c_int = 49;
pub const SO_ATTACH_BPF: ::c_int = 50;
pub const SO_DETACH_BPF: ::c_int = SO_DETACH_FILTER;
pub const SO_ATTACH_REUSEPORT_CBPF: ::c_int = 51;
pub const SO_ATTACH_REUSEPORT_EBPF: ::c_int = 52;
pub const SO_CNX_ADVICE: ::c_int = 53;
pub const SCM_TIMESTAMPING_OPT_STATS: ::c_int = 54;
pub const SO_MEMINFO: ::c_int = 55;
pub const SO_INCOMING_NAPI_ID: ::c_int = 56;
pub const SO_COOKIE: ::c_int = 57;
pub const SCM_TIMESTAMPING_PKTINFO: ::c_int = 58;
pub const SO_PEERGROUPS: ::c_int = 59;
pub const SO_ZEROCOPY: ::c_int = 60;
pub const SO_TXTIME: ::c_int = 61;
pub const SCM_TXTIME: ::c_int = SO_TXTIME;
pub const SO_BINDTOIFINDEX: ::c_int = 62;
// pub const SO_TIMESTAMP_NEW: ::c_int = 63;
// pub const SO_TIMESTAMPNS_NEW: ::c_int = 64;
// pub const SO_TIMESTAMPING_NEW: ::c_int = 65;
// pub const SO_RCVTIMEO_NEW: ::c_int = 66;
// pub const SO_SNDTIMEO_NEW: ::c_int = 67;
// pub const SO_DETACH_REUSEPORT_BPF: ::c_int = 68;
// pub const SO_PREFER_BUSY_POLL: ::c_int = 69;
// pub const SO_BUSY_POLL_BUDGET: ::c_int = 70;

// Defined in unix/linux_like/mod.rs
// pub const SCM_TIMESTAMP: ::c_int = SO_TIMESTAMP;
pub const SCM_TIMESTAMPNS: ::c_int = SO_TIMESTAMPNS;
pub const SCM_TIMESTAMPING: ::c_int = SO_TIMESTAMPING;

pub const TIOCMGET: ::Ioctl = 0x5415;
pub const TIOCMBIS: ::Ioctl = 0x5416;
pub const TIOCMBIC: ::Ioctl = 0x5417;
pub const TIOCMSET: ::Ioctl = 0x5418;

pub const TIOCM_LE: ::c_int = 0x001;
pub const TIOCM_DTR: ::c_int = 0x002;
pub const TIOCM_RTS: ::c_int = 0x004;
pub const TIOCM_ST: ::c_int = 0x008;
pub const TIOCM_SR: ::c_int = 0x010;
pub const TIOCM_CTS: ::c_int = 0x020;
pub const TIOCM_CAR: ::c_int = 0x040;
pub const TIOCM_CD: ::c_int = TIOCM_CAR;
pub const TIOCM_RNG: ::c_int = 0x080;
pub const TIOCM_RI: ::c_int = TIOCM_RNG;
pub const TIOCM_DSR: ::c_int = 0x100;

pub const BOTHER: ::speed_t = 0o0037;
pub const IBSHIFT: ::tcflag_t = 16;

pub const BLKSSZGET: ::c_int = 0x20001268;
pub const BLKPBSZGET: ::c_int = 0x2000127B;
<|endoftext|>


<commit_before>mod terminal;
mod ansi;

use std::io::{self, Write, BufWriter};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, color, cursor, style};
use rayon::prelude::*;
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    Exit,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Options {
    pub choices: Vec<String>,
    pub initial: String,
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    width: usize,
    terminal: Terminal,
}

impl Interface {
    /// Creates a new Interface from the provided options.
    pub fn from_opts(opts: Options) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: opts.choices,
            matching: Vec::new(),
            selected: 0,
            search: opts.initial,
            width: term.max_width,
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') | Key::Ctrl('d') => {
                        self.clear()?;
                        return Err(Error::Exit);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_existing_matches();
                        self.render()?;
                    },

                    Key::Backspace | Key::Ctrl('h') => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    Key::Ctrl('u') => {
                        self.search.clear();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            par_iter().
            cloned().
            filter_map(|choice| Choice::with_positions(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn filter_existing_matches(&mut self) {
        let mut matches = self.matching.
            par_iter().
            map(|c| c.text().to_string()).
            filter_map(|choice| Choice::with_positions(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn render(&mut self) -> io::Result<()> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        write!(self.terminal, "> {}", self.search)?;
        self.render_matches()?;
        Ok(())
    }

    fn render_matches(&mut self) -> io::Result<()> {
        let mut term = BufWriter::new(&mut self.terminal);
        let matches = self.matching.iter().take(10);
        let n = matches.len() as u16;

        for (i, choice) in matches.enumerate() {
            let selected = i == self.selected;
            let chars = choice.text().chars().take(self.width);

            write!(term, "\r\n")?;

            if selected {
                write!(term, "{}", style::Invert)?;
            }

            if let Some(positions) = choice.positions() {
                for (i, ch) in chars.enumerate() {
                    let match_position = positions.iter().any(|p| *p == i);

                    if match_position {
                        let color = color::Fg(color::Colors::Magenta);
                        let reset = color::Fg(color::Reset);
                        write!(term, "{}{}{}", color, ch, reset)?;
                    } else {
                        write!(term, "{}", ch)?;
                    }
                }

            } else {
                write!(term, "{}", chars.collect::<String>())?;
            }

            if selected {
                write!(term, "{}", style::NoInvert)?;
            }
        }

        if n > 0 {
            let column = format!("> {}", self.search).len() as u16;
            write!(term, "{}{}", cursor::Up(n), cursor::Column(column + 1))?;
        }

        Ok(())
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<commit_msg>Coalesce render methods, use BufWriter everywhere<commit_after>mod terminal;
mod ansi;

use std::io::{self, Write, BufWriter};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, color, cursor, style};
use rayon::prelude::*;
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    Exit,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Options {
    pub choices: Vec<String>,
    pub initial: String,
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    width: usize,
    terminal: Terminal,
}

impl Interface {
    /// Creates a new Interface from the provided options.
    pub fn from_opts(opts: Options) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: opts.choices,
            matching: Vec::new(),
            selected: 0,
            search: opts.initial,
            width: term.max_width,
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') | Key::Ctrl('d') => {
                        self.clear()?;
                        return Err(Error::Exit);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_existing_matches();
                        self.render()?;
                    },

                    Key::Backspace | Key::Ctrl('h') => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    Key::Ctrl('u') => {
                        self.search.clear();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            par_iter().
            cloned().
            filter_map(|choice| Choice::with_positions(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn filter_existing_matches(&mut self) {
        let mut matches = self.matching.
            par_iter().
            map(|c| c.text().to_string()).
            filter_map(|choice| Choice::with_positions(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn prompt(&self) -> String {
        format!("> {}", self.search)
    }

    fn render(&mut self) -> io::Result<()> {
        let prompt = self.prompt();
        let matches = self.matching.iter().take(10);
        let n = matches.len() as u16;

        let mut term = BufWriter::new(&mut self.terminal);

        write!(term, "{}{}{}", cursor::Column(1), clear::Screen, prompt)?;

        for (i, choice) in matches.enumerate() {
            write!(term, "\r\n")?;

            let selected = i == self.selected;
            let chars = choice.text().chars().take(self.width);

            if selected {
                write!(term, "{}", style::Invert)?;
            }

            if let Some(positions) = choice.positions() {
                for (i, ch) in chars.enumerate() {
                    let match_position = positions.iter().any(|p| *p == i);

                    if match_position {
                        let color = color::Fg(color::Colors::Magenta);
                        let reset = color::Fg(color::Reset);
                        write!(term, "{}{}{}", color, ch, reset)?;
                    } else {
                        write!(term, "{}", ch)?;
                    }
                }

            } else {
                write!(term, "{}", chars.collect::<String>())?;
            }

            if selected {
                write!(term, "{}", style::NoInvert)?;
            }
        }

        if n > 0 {
            let col = (prompt.len() + 1) as u16;
            write!(term, "{}{}", cursor::Up(n), cursor::Column(col))?;
        }

        Ok(())
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<|endoftext|>


<commit_before>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;
extern crate chrono;

use std::io::{self, Write};
use std::process;
use std::path::Path;
use docopt::Docopt;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision show-xml <uuid> [<directory>]
  mprovision show-expired [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
                   .and_then(|d| {
                       d.options_first(true)
                        .version(Some(format!("mprovision {}", version!())))
                        .parse()
                   })
                   .unwrap_or_else(|e| e.exit());

    if let Some(cmd) = Command::from_args(&args) {
        let result = match cmd {
            Command::Search => search(&args),
            Command::Remove => remove(&args),
            Command::ShowXml => show_xml(&args),
            Command::ShowExpired => show_expired(&args),
        };
        match result {
            Ok(_) => process::exit(0),
            Err(e) => {
                writeln!(&mut io::stderr(), "{}", e).unwrap();
                process::exit(1);
            }
        }
    }
}

enum Command {
    Search,
    Remove,
    ShowXml,
    ShowExpired,
}

impl Command {
    fn from_args(args: &::docopt::ArgvMap) -> Option<Command> {
        if args.get_bool("search") {
            Some(Command::Search)
        } else if args.get_bool("remove") {
            Some(Command::Remove)
        } else if args.get_bool("show-xml") {
            Some(Command::ShowXml)
        } else if args.get_bool("show-expired") {
            Some(Command::ShowExpired)
        } else {
            None
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::search(path, text))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("Nothing found for '{}'", text);
            } else {
                println!("Found {} of {} profiles.\n",
                         info.profiles.len(),
                         info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::remove(path, uuid))
        .and_then(|_| Ok(println!("Profile '{}' was removed.", uuid)))
        .map_err(|e| e.to_string())
}

fn show_xml(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::xml(path, uuid))
        .and_then(|xml| Ok(println!("{}", xml)))
        .map_err(|e| e.to_string())
}

fn show_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    mprovision::with_path(directory(args),
                          |path| mprovision::expired_profiles(path, UTC::now()))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("All profiles are valid.");
            } else {
                println!("Found {} of {} profiles.\n",
                         info.profiles.len(),
                         info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn directory(args: &::docopt::ArgvMap) -> Option<&Path> {
    let dir_name = args.get_str("<directory>");
    if dir_name.is_empty() {
        None
    } else {
        Some(dir_name.as_ref())
    }
}
<commit_msg>Implement command to remove expired profiles.<commit_after>
extern crate mprovision;
extern crate docopt;
#[macro_use(version)]
extern crate version;
extern crate chrono;

use std::io::{self, Write};
use std::process;
use std::path::Path;
use docopt::Docopt;

const USAGE: &'static str = "
mprovision
A tool that helps iOS developers to manage mobileprovision files.

Usage:
  mprovision search <text> [<directory>]
  mprovision remove <uuid> [<directory>]
  mprovision show-xml <uuid> [<directory>]
  mprovision show-expired [<directory>]
  mprovision remove-expired [<directory>]
  mprovision (-h | --help)
  mprovision --version

Options:
  -h --help     Show this help message.
  --version     Show version.
";

fn main() {
    let args = Docopt::new(USAGE)
                   .and_then(|d| {
                       d.options_first(true)
                        .version(Some(format!("mprovision {}", version!())))
                        .parse()
                   })
                   .unwrap_or_else(|e| e.exit());

    if let Some(cmd) = Command::from_args(&args) {
        let result = match cmd {
            Command::Search => search(&args),
            Command::Remove => remove(&args),
            Command::ShowXml => show_xml(&args),
            Command::ShowExpired => show_expired(&args),
            Command::RemoveExpired => remove_expired(&args),
        };
        match result {
            Ok(_) => process::exit(0),
            Err(e) => {
                writeln!(&mut io::stderr(), "{}", e).unwrap();
                process::exit(1);
            }
        }
    }
}

enum Command {
    Search,
    Remove,
    ShowXml,
    ShowExpired,
    RemoveExpired,
}

impl Command {
    fn from_args(args: &::docopt::ArgvMap) -> Option<Command> {
        if args.get_bool("search") {
            Some(Command::Search)
        } else if args.get_bool("remove") {
            Some(Command::Remove)
        } else if args.get_bool("show-xml") {
            Some(Command::ShowXml)
        } else if args.get_bool("show-expired") {
            Some(Command::ShowExpired)
        } else if args.get_bool("remove-expired") {
            Some(Command::RemoveExpired)
        } else {
            None
        }
    }
}

fn search(args: &::docopt::ArgvMap) -> Result<(), String> {
    let text = args.get_str("<text>");
    if text.is_empty() {
        return Err("<text> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::search(path, text))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("Nothing found for '{}'", text);
            } else {
                println!("Found {} of {} profiles.\n",
                         info.profiles.len(),
                         info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::remove(path, uuid))
        .and_then(|_| Ok(println!("Profile '{}' was removed.", uuid)))
        .map_err(|e| e.to_string())
}

fn show_xml(args: &::docopt::ArgvMap) -> Result<(), String> {
    let uuid = args.get_str("<uuid>");
    if uuid.is_empty() {
        return Err("<uuid> should not be empty.".to_owned());
    }

    mprovision::with_path(directory(args), |path| mprovision::xml(path, uuid))
        .and_then(|xml| Ok(println!("{}", xml)))
        .map_err(|e| e.to_string())
}

fn show_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    mprovision::with_path(directory(args),
                          |path| mprovision::expired_profiles(path, UTC::now()))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("All profiles are valid.");
            } else {
                println!("Found {} of {} profiles.\n",
                         info.profiles.len(),
                         info.total);
                for profile in &info.profiles {
                    println!("{}\n", profile.description());
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn remove_expired(args: &::docopt::ArgvMap) -> Result<(), String> {
    use chrono::*;

    mprovision::with_path(directory(args),
                          |path| mprovision::expired_profiles(path, UTC::now()))
        .and_then(|info| {
            if info.profiles.len() == 0 {
                println!("All profiles are valid.");
            } else {
                println!("Found {} of {} profiles.\n",
                         info.profiles.len(),
                         info.total);
                for profile in info.profiles {
                    match std::fs::remove_file(&profile.path) {
                        Ok(_) => println!("Profile '{}' was removed.", profile.uuid),
                        Err(e) => {
                            println!("Error while trying to remove profile '{}'", profile.uuid);
                            println!("{}", e);
                        }
                    }
                }
            }
            Ok(())
        })
        .map_err(|e| e.to_string())
}

fn directory(args: &::docopt::ArgvMap) -> Option<&Path> {
    let dir_name = args.get_str("<directory>");
    if dir_name.is_empty() {
        None
    } else {
        Some(dir_name.as_ref())
    }
}
<|endoftext|>