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

//! Terminfo database interface.

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

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

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

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

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

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

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

    /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
    pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut io::Write) -> Result<()> {
        match self.strings.get(cmd) {
            Some(cmd) => {
                match expand(cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }

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

}

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

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

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

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

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

pub mod searcher;

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

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

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

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

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

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

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

    fn reset(&mut self) -> Result<()> {
        self.ti.reset(&mut self.out)
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

//! Terminfo database interface.

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

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

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

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

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

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

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

    /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
    pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut io::Write) -> Result<()> {
        match self.strings.get(cmd) {
            Some(cmd) => {
                match expand(cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }

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

}

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

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

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

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

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

pub mod searcher;

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

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

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

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

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

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

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

    fn reset(&mut self) -> Result<()> {
        self.ti.reset(&mut self.out)
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

use byteorder;
use prelude::*;

impl Uuid {
    /// Creates a new [`Uuid`] from a `u128` value.
    ///
    /// To create a [`Uuid`] from `u128`s, you need `u128` feature enabled for this crate.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    #[inline]
    pub fn from_u128(quad: u128) -> Self {
        Uuid::from(quad)
    }
}

impl From<u128> for Uuid {
    fn from(f: u128) -> Self {
        let mut bytes = [0; 16];

        {
            use byteorder::ByteOrder;

            byteorder::NativeEndian::write_u128(&mut bytes[..], f);
        }

        Uuid { bytes }
    }
}
<commit_msg>add test for u128<commit_after>// Copyright 2013-2014 The Rust Project Developers.
// Copyright 2018 The Uuid Project Developers.
//
// See the COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder;
use prelude::*;

impl Uuid {
    /// Creates a new [`Uuid`] from a `u128` value.
    ///
    /// To create a [`Uuid`] from `u128`s, you need `u128` feature enabled for this crate.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    #[inline]
    pub fn from_u128(quad: u128) -> Self {
        Uuid::from(quad)
    }
}

impl From<u128> for Uuid {
    fn from(f: u128) -> Self {
        let mut bytes = [0; 16];

        {
            use byteorder::ByteOrder;

            byteorder::NativeEndian::write_u128(&mut bytes[..], f);
        }

        Uuid { bytes }
    }
}

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

    #[test]
    fn test_from_u128() {
        const U128: u128 = 0x3a0724b4_93a0_4d87_ac28_759c6caa13c4;

        let uuid = Uuid::from(U128);

        let uuid2: Uuid = U128.into();

        assert_eq!(uuid, uuid2)
    }

}
<|endoftext|>


<commit_before>extern crate winapi;
extern crate user32;
extern crate kernel32;

use std::ffi::OsStr;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use self::winapi::winuser::{WNDCLASSW, HWND_MESSAGE, WM_USER, WM_DESTROY};
use self::winapi::minwindef::{DWORD, UINT, LRESULT, LPARAM, WPARAM};
use self::winapi::windef::{HWND, HICON};
use self::winapi::winnt::WCHAR;
use self::winapi::{GUID, SW_HIDE};

const NIF_ICON: UINT   = 0x2;
const NIF_TIP: UINT    = 0x4;
const NIF_INFO: UINT   = 0x10;
const NIIF_USER: UINT = 0x4;

const ERROR_CLASS_ALREADY_EXISTS: UINT = 1410;

#[repr(C)]
#[allow(non_snake_case)]
pub struct NOTIFYICONDATA {
    pub cbSize:           DWORD,
    pub hWnd:             HWND,
    pub uID:              UINT,
    pub uFlags:           UINT,
    pub uCallbackMessage: UINT,
    pub hIcon:            HICON,
    pub szTip:            [WCHAR; 128],
    pub dwState:          DWORD,
    pub dwStateMask:      DWORD,
    pub szInfo:           [WCHAR; 256],
    pub uVersion:         UINT,
    pub szInfoTitle:      [WCHAR; 64],
    pub dwInfoFlags:      DWORD,
    pub guidItem:         GUID,
    pub hBalloonIcon:     HICON,
}

#[link(name = "shell32")]
extern {
    pub fn Shell_NotifyIconW(mode: DWORD, iconData: *const NOTIFYICONDATA);
}

// We cannot implement the sync trait for arbitrary types (see E0117), so we use a struct to bypass it
pub struct NotifData {
    pub data: NOTIFYICONDATA,
}

impl NotifData {
    pub unsafe fn new() -> NotifData {
        let class_name = to_wstring(env!("CARGO_PKG_NAME"));

        let window = WNDCLASSW {
            style:         0,
            lpfnWndProc:   Some(window_proc),
            cbClsExtra:    0,
            cbWndExtra:    0,
            hInstance:     ptr::null_mut(),
            hIcon:         ptr::null_mut(),
            hCursor:       ptr::null_mut(),
            hbrBackground: ptr::null_mut(),
            lpszMenuName:  ptr::null(),
            lpszClassName: class_name.as_ptr(),
        };

        if user32::RegisterClassW(&window) == 0 {
            let error = kernel32::GetLastError() as UINT;

            // No reason to block the user from opening multiple program instances
            if error != ERROR_CLASS_ALREADY_EXISTS {
                panic!("unable to register notification window class: error {}", error);
            }
        }

        let h_wnd = user32::CreateWindowExW(
                        0,
                        class_name.as_ptr(),
                        class_name.as_ptr(),
                        0, 0, 0, 0, 0,
                        HWND_MESSAGE,
                        ptr::null_mut(),
                        ptr::null_mut(),
                        ptr::null_mut());

        if h_wnd.is_null() {
            panic!("unable to create notification window: error {}", kernel32::GetLastError());
        }

        let notif_data = NOTIFYICONDATA {
            cbSize:           mem::size_of::<NOTIFYICONDATA>() as DWORD,
            hWnd:             h_wnd,
            uID:              1,
            uFlags:           NIF_ICON | NIF_INFO | NIF_TIP,
            uCallbackMessage: WM_USER + 200,
            hIcon:            ptr::null_mut(),
            szTip:            [0; 128],
            dwState:          0,
            dwStateMask:      0,
            szInfo:           [0; 256],
            uVersion:         4,
            szInfoTitle:      [0; 64],
            dwInfoFlags:      NIIF_USER,
            guidItem:         winapi::GUID { Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8] },
            hBalloonIcon:     0 as winapi::HICON,
        };

        NotifData {
            data: notif_data,
        }
    }
}

unsafe impl Send for NotifData {}
unsafe impl Sync for NotifData {}

pub fn to_wstring(str : &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
}

unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT
{
    if msg == WM_DESTROY {
        user32::PostQuitMessage(0);
    }

    user32::DefWindowProcW(h_wnd, msg, w_param, l_param)
}

pub fn close_console() {
    unsafe {
        let window = kernel32::GetConsoleWindow();

        if !window.is_null() {
            user32::ShowWindow(window, SW_HIDE);
        }
    }
}<commit_msg>Fix appalling indentation<commit_after>extern crate winapi;
extern crate user32;
extern crate kernel32;

use std::ffi::OsStr;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use self::winapi::winuser::{WNDCLASSW, HWND_MESSAGE, WM_USER, WM_DESTROY};
use self::winapi::minwindef::{DWORD, UINT, LRESULT, LPARAM, WPARAM};
use self::winapi::windef::{HWND, HICON};
use self::winapi::winnt::WCHAR;
use self::winapi::{GUID, SW_HIDE};

const NIF_ICON:  UINT = 0x2;
const NIF_TIP:   UINT = 0x4;
const NIF_INFO:  UINT = 0x10;
const NIIF_USER: UINT = 0x4;

const ERROR_CLASS_ALREADY_EXISTS: UINT = 1410;

#[repr(C)]
#[allow(non_snake_case)]
pub struct NOTIFYICONDATA {
    pub cbSize:           DWORD,
    pub hWnd:             HWND,
    pub uID:              UINT,
    pub uFlags:           UINT,
    pub uCallbackMessage: UINT,
    pub hIcon:            HICON,
    pub szTip:            [WCHAR; 128],
    pub dwState:          DWORD,
    pub dwStateMask:      DWORD,
    pub szInfo:           [WCHAR; 256],
    pub uVersion:         UINT,
    pub szInfoTitle:      [WCHAR; 64],
    pub dwInfoFlags:      DWORD,
    pub guidItem:         GUID,
    pub hBalloonIcon:     HICON,
}

#[link(name = "shell32")]
extern {
    pub fn Shell_NotifyIconW(mode: DWORD, iconData: *const NOTIFYICONDATA);
}

// We cannot implement the sync trait for arbitrary types (see E0117), so we use a struct to bypass it
pub struct NotifData {
    pub data: NOTIFYICONDATA,
}

impl NotifData {
    pub unsafe fn new() -> NotifData {
        let class_name = to_wstring(env!("CARGO_PKG_NAME"));

        let window = WNDCLASSW {
            style:         0,
            lpfnWndProc:   Some(window_proc),
            cbClsExtra:    0,
            cbWndExtra:    0,
            hInstance:     ptr::null_mut(),
            hIcon:         ptr::null_mut(),
            hCursor:       ptr::null_mut(),
            hbrBackground: ptr::null_mut(),
            lpszMenuName:  ptr::null(),
            lpszClassName: class_name.as_ptr(),
        };

        if user32::RegisterClassW(&window) == 0 {
            let error = kernel32::GetLastError() as UINT;

            // No reason to block the user from opening multiple program instances
            if error != ERROR_CLASS_ALREADY_EXISTS {
                panic!("unable to register notification window class: error {}", error);
            }
        }

        let h_wnd = user32::CreateWindowExW(
                        0,
                        class_name.as_ptr(),
                        class_name.as_ptr(),
                        0, 0, 0, 0, 0,
                        HWND_MESSAGE,
                        ptr::null_mut(),
                        ptr::null_mut(),
                        ptr::null_mut());

        if h_wnd.is_null() {
            panic!("unable to create notification window: error {}", kernel32::GetLastError());
        }

        let notif_data = NOTIFYICONDATA {
            cbSize:           mem::size_of::<NOTIFYICONDATA>() as DWORD,
            hWnd:             h_wnd,
            uID:              1,
            uFlags:           NIF_ICON | NIF_INFO | NIF_TIP,
            uCallbackMessage: WM_USER + 200,
            hIcon:            ptr::null_mut(),
            szTip:            [0; 128],
            dwState:          0,
            dwStateMask:      0,
            szInfo:           [0; 256],
            uVersion:         4,
            szInfoTitle:      [0; 64],
            dwInfoFlags:      NIIF_USER,
            guidItem:         winapi::GUID { Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8] },
            hBalloonIcon:     0 as winapi::HICON,
        };

        NotifData {
            data: notif_data,
        }
    }
}

unsafe impl Send for NotifData {}
unsafe impl Sync for NotifData {}

pub fn to_wstring(str : &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
}

unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT
{
    if msg == WM_DESTROY {
        user32::PostQuitMessage(0);
    }

    user32::DefWindowProcW(h_wnd, msg, w_param, l_param)
}

pub fn close_console() {
    unsafe {
        let window = kernel32::GetConsoleWindow();

        if !window.is_null() {
            user32::ShowWindow(window, SW_HIDE);
        }
    }
}<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate duct;

use std::env::{args, current_dir, set_current_dir};
use std::io;
use std::path::Path;
use std::process::exit;

#[cfg(unix)]
fn check_executable(path: &Path) {
    use std::os::unix::fs::MetadataExt;
    const EXECUTABLE_BIT: u32 = 0o100;
    let metadata = path.metadata().unwrap();
    let mode = metadata.mode();
    if mode & EXECUTABLE_BIT == 0 {
        println!("Expected {:?} to be publicly executable, but found mode {:o}.",
                 path,
                 mode);
        exit(1);
    }
}

#[cfg(windows)]
fn check_executable(path: &Path) {
    // no-op
}

fn main() {
    // The first command line arg is a path to an executable, and the rest are
    // its arguments. Chdir to the parent dir, and then try to execute the
    // program as a single-component Path (*not* a string). Duct should
    // automatically add the leading ./ for us so that it works on Unix. (And if
    // the executable doesn't actually exist, duct should prevent it from
    // matching some other command in the PATH, on either Unix or Windows.)

    let args_vec: Vec<_> = args().collect();
    if args_vec.len() < 2 {
        println!("must have at least 1 exe arg");
        exit(1);
    }

    // Parse out the exe name and the dir it's in.
    let exe_full_path = Path::new(&args_vec[1]);
    let exe_parent = exe_full_path.parent().unwrap();
    let exe_name: &Path = exe_full_path.file_name().unwrap().as_ref();

    // Sanity check that exe actually exists.
    if !exe_full_path.is_file() {
        println!(r#""{}" is not a file."#, exe_full_path.display());
        exit(1);
    }

    // If we try to execute a non-executable file on Unix, we'll end up with a
    // "file not found" error. We really don't want to confuse those with the
    // errors we get from bad Path handling. Explicitly check for that here.
    check_executable(exe_full_path);

    // Chdir to that dir.
    set_current_dir(exe_parent).unwrap();

    // Run the child!
    println!("executing command {:?} in dir {:?}",
             exe_name,
             current_dir().unwrap());
    let res = duct::cmd(exe_name, &args_vec[2..]).run();

    // Check what the child did, and exit appropriately.

    if res.is_ok() {
        println!("Success!");
        exit(0);
    }

    // The point of this whole test is to see if the child executes at all, so
    // we probably don't care about errors it returns. Still, it's safer to pass
    // them along then to drop them. We probably won't write tests that hit this
    // branch.
    if let Err(duct::Error::Status(output)) = res {
        let status = output.status.code().unwrap();
        println!(r#"command "{}" exited with error code {}"#,
                 exe_name.display(),
                 status);
        exit(status);
    }

    if let Err(duct::Error::Io(ioerror)) = res {
        // Here's the error we're *really* looking for.
        if ioerror.kind() == io::ErrorKind::NotFound {
            println!("File not found during execution! Path sanitization is TOTALLY BROKEN!");
            exit(1);
        } else {
            println!("Unexpected IO error: {:?}", ioerror);
            exit(1);
        }
    }

    // Some unexpected error? (UTF8 errors shouldn't be possible here.)
    res.unwrap();
}
<commit_msg>clean up a warning on Windows<commit_after>#![deny(warnings)]

extern crate duct;

use std::env::{args, current_dir, set_current_dir};
use std::io;
use std::path::Path;
use std::process::exit;

#[cfg(unix)]
fn check_executable(path: &Path) {
    use std::os::unix::fs::MetadataExt;
    const EXECUTABLE_BIT: u32 = 0o100;
    let metadata = path.metadata().unwrap();
    let mode = metadata.mode();
    if mode & EXECUTABLE_BIT == 0 {
        println!("Expected {:?} to be publicly executable, but found mode {:o}.",
                 path,
                 mode);
        exit(1);
    }
}

#[cfg(windows)]
fn check_executable(_: &Path) {
    // no-op
}

fn main() {
    // The first command line arg is a path to an executable, and the rest are
    // its arguments. Chdir to the parent dir, and then try to execute the
    // program as a single-component Path (*not* a string). Duct should
    // automatically add the leading ./ for us so that it works on Unix. (And if
    // the executable doesn't actually exist, duct should prevent it from
    // matching some other command in the PATH, on either Unix or Windows.)

    let args_vec: Vec<_> = args().collect();
    if args_vec.len() < 2 {
        println!("must have at least 1 exe arg");
        exit(1);
    }

    // Parse out the exe name and the dir it's in.
    let exe_full_path = Path::new(&args_vec[1]);
    let exe_parent = exe_full_path.parent().unwrap();
    let exe_name: &Path = exe_full_path.file_name().unwrap().as_ref();

    // Sanity check that exe actually exists.
    if !exe_full_path.is_file() {
        println!(r#""{}" is not a file."#, exe_full_path.display());
        exit(1);
    }

    // If we try to execute a non-executable file on Unix, we'll end up with a
    // "file not found" error. We really don't want to confuse those with the
    // errors we get from bad Path handling. Explicitly check for that here.
    check_executable(exe_full_path);

    // Chdir to that dir.
    set_current_dir(exe_parent).unwrap();

    // Run the child!
    println!("executing command {:?} in dir {:?}",
             exe_name,
             current_dir().unwrap());
    let res = duct::cmd(exe_name, &args_vec[2..]).run();

    // Check what the child did, and exit appropriately.

    if res.is_ok() {
        println!("Success!");
        exit(0);
    }

    // The point of this whole test is to see if the child executes at all, so
    // we probably don't care about errors it returns. Still, it's safer to pass
    // them along then to drop them. We probably won't write tests that hit this
    // branch.
    if let Err(duct::Error::Status(output)) = res {
        let status = output.status.code().unwrap();
        println!(r#"command "{}" exited with error code {}"#,
                 exe_name.display(),
                 status);
        exit(status);
    }

    if let Err(duct::Error::Io(ioerror)) = res {
        // Here's the error we're *really* looking for.
        if ioerror.kind() == io::ErrorKind::NotFound {
            println!("File not found during execution! Path sanitization is TOTALLY BROKEN!");
            exit(1);
        } else {
            println!("Unexpected IO error: {:?}", ioerror);
            exit(1);
        }
    }

    // Some unexpected error? (UTF8 errors shouldn't be possible here.)
    res.unwrap();
}
<|endoftext|>


<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::def_id::DefId;

enum EdgeKind {
    Unwind,
    Other,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.def_id,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_bb(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Other)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along any edges
                | (true, true, _) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!("encountered jump that does not respect unwind invariants {:?}", bb)
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(location, *target, EdgeKind::Other);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(location, *resume, EdgeKind::Other);
                if let Some(drop) = drop {
                    self.check_bb(location, *drop, EdgeKind::Other);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                self.check_bb(location, *imaginary_target, EdgeKind::Other);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(location, *destination, EdgeKind::Other);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>Strengthen cleanup to cleanup check<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::def_id::DefId;

enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.def_id,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_bb(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!("encountered jump that does not respect unwind invariants {:?}", bb)
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_bb(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_bb(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_bb(location, *real_target, EdgeKind::Normal);
                self.check_bb(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>


<commit_before>use super::{StringReader, UnmatchedBrace};

use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::{
    DelimSpan,
    IsJoint::{self, *},
    TokenStream, TokenTree, TreeAndJoint,
};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
use rustc_span::Span;

impl<'a> StringReader<'a> {
    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
        let mut tt_reader = TokenTreesReader {
            string_reader: self,
            token: Token::dummy(),
            joint_to_prev: Joint,
            open_braces: Vec::new(),
            unmatched_braces: Vec::new(),
            matching_delim_spans: Vec::new(),
            last_unclosed_found_span: None,
            last_delim_empty_block_spans: FxHashMap::default(),
        };
        let res = tt_reader.parse_all_token_trees();
        (res, tt_reader.unmatched_braces)
    }
}

struct TokenTreesReader<'a> {
    string_reader: StringReader<'a>,
    token: Token,
    joint_to_prev: IsJoint,
    /// Stack of open delimiters and their spans. Used for error message.
    open_braces: Vec<(token::DelimToken, Span)>,
    unmatched_braces: Vec<UnmatchedBrace>,
    /// The type and spans for all braces
    ///
    /// Used only for error recovery when arriving to EOF with mismatched braces.
    matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
    last_unclosed_found_span: Option<Span>,
    /// Collect empty block spans that might have been auto-inserted by editors.
    last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
}

impl<'a> TokenTreesReader<'a> {
    // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
    fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
        let mut buf = TokenStreamBuilder::default();

        self.real_token();
        while self.token != token::Eof {
            buf.push(self.parse_token_tree()?);
        }

        Ok(buf.into_token_stream())
    }

    // Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
    fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
        let mut buf = TokenStreamBuilder::default();
        loop {
            if let token::CloseDelim(..) = self.token.kind {
                return buf.into_token_stream();
            }

            match self.parse_token_tree() {
                Ok(tree) => buf.push(tree),
                Err(mut e) => {
                    e.emit();
                    return buf.into_token_stream();
                }
            }
        }
    }

    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
        let sm = self.string_reader.sess.source_map();
        match self.token.kind {
            token::Eof => {
                let msg = "this file contains an unclosed delimiter";
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
                for &(_, sp) in &self.open_braces {
                    err.span_label(sp, "unclosed delimiter");
                    self.unmatched_braces.push(UnmatchedBrace {
                        expected_delim: token::DelimToken::Brace,
                        found_delim: None,
                        found_span: self.token.span,
                        unclosed_span: Some(sp),
                        candidate_span: None,
                    });
                }

                if let Some((delim, _)) = self.open_braces.last() {
                    if let Some((_, open_sp, close_sp)) =
                        self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
                            if let Some(close_padding) = sm.span_to_margin(*close_sp) {
                                if let Some(open_padding) = sm.span_to_margin(*open_sp) {
                                    return delim == d && close_padding != open_padding;
                                }
                            }
                            false
                        })
                    // these are in reverse order as they get inserted on close, but
                    {
                        // we want the last open/first close
                        err.span_label(*open_sp, "this delimiter might not be properly closed...");
                        err.span_label(
                            *close_sp,
                            "...as it matches this but it has different indentation",
                        );
                    }
                }
                Err(err)
            }
            token::OpenDelim(delim) => {
                // The span for beginning of the delimited section
                let pre_span = self.token.span;

                // Parse the open delimiter.
                self.open_braces.push((delim, self.token.span));
                self.real_token();

                // Parse the token trees within the delimiters.
                // We stop at any delimiter so we can try to recover if the user
                // uses an incorrect delimiter.
                let tts = self.parse_token_trees_until_close_delim();

                // Expand to cover the entire delimited token tree
                let delim_span = DelimSpan::from_pair(pre_span, self.token.span);

                match self.token.kind {
                    // Correct delimiter.
                    token::CloseDelim(d) if d == delim => {
                        let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
                        let close_brace_span = self.token.span;

                        if tts.is_empty() {
                            let empty_block_span = open_brace_span.to(close_brace_span);
                            if !sm.is_multiline(empty_block_span) {
                                // Only track if the block is in the form of `{}`, otherwise it is
                                // likely that it was written on purpose.
                                self.last_delim_empty_block_spans.insert(delim, empty_block_span);
                            }
                        }

                        if self.open_braces.is_empty() {
                            // Clear up these spans to avoid suggesting them as we've found
                            // properly matched delimiters so far for an entire block.
                            self.matching_delim_spans.clear();
                        } else {
                            self.matching_delim_spans.push((
                                open_brace,
                                open_brace_span,
                                close_brace_span,
                            ));
                        }
                        // Parse the closing delimiter.
                        self.real_token();
                    }
                    // Incorrect delimiter.
                    token::CloseDelim(other) => {
                        let mut unclosed_delimiter = None;
                        let mut candidate = None;
                        if self.last_unclosed_found_span != Some(self.token.span) {
                            // do not complain about the same unclosed delimiter multiple times
                            self.last_unclosed_found_span = Some(self.token.span);
                            // This is a conservative error: only report the last unclosed
                            // delimiter. The previous unclosed delimiters could actually be
                            // closed! The parser just hasn't gotten to them yet.
                            if let Some(&(_, sp)) = self.open_braces.last() {
                                unclosed_delimiter = Some(sp);
                            };
                            if let Some(current_padding) = sm.span_to_margin(self.token.span) {
                                for (brace, brace_span) in &self.open_braces {
                                    if let Some(padding) = sm.span_to_margin(*brace_span) {
                                        // high likelihood of these two corresponding
                                        if current_padding == padding && brace == &other {
                                            candidate = Some(*brace_span);
                                        }
                                    }
                                }
                            }
                            let (tok, _) = self.open_braces.pop().unwrap();
                            self.unmatched_braces.push(UnmatchedBrace {
                                expected_delim: tok,
                                found_delim: Some(other),
                                found_span: self.token.span,
                                unclosed_span: unclosed_delimiter,
                                candidate_span: candidate,
                            });
                        } else {
                            self.open_braces.pop();
                        }

                        // If the incorrect delimiter matches an earlier opening
                        // delimiter, then don't consume it (it can be used to
                        // close the earlier one). Otherwise, consume it.
                        // E.g., we try to recover from:
                        // fn foo() {
                        //     bar(baz(
                        // }  // Incorrect delimiter but matches the earlier `{`
                        if !self.open_braces.iter().any(|&(b, _)| b == other) {
                            self.real_token();
                        }
                    }
                    token::Eof => {
                        // Silently recover, the EOF token will be seen again
                        // and an error emitted then. Thus we don't pop from
                        // self.open_braces here.
                    }
                    _ => {}
                }

                Ok(TokenTree::Delimited(delim_span, delim, tts).into())
            }
            token::CloseDelim(delim) => {
                // An unexpected closing delimiter (i.e., there is no
                // matching opening delimiter).
                let token_str = token_to_string(&self.token);
                let msg = format!("unexpected closing delimiter: `{}`", token_str);
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);

                if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
                    err.span_label(
                        span,
                        "this block is empty, you might have not meant to close it",
                    );
                }
                err.span_label(self.token.span, "unexpected closing delimiter");
                Err(err)
            }
            _ => {
                let tt = TokenTree::Token(self.token.take());
                self.real_token();
                let is_joint = self.joint_to_prev == Joint && self.token.is_op();
                Ok((tt, if is_joint { Joint } else { NonJoint }))
            }
        }
    }

    fn real_token(&mut self) {
        self.joint_to_prev = Joint;
        loop {
            let token = self.string_reader.next_token();
            match token.kind {
                token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
                    self.joint_to_prev = NonJoint;
                }
                _ => {
                    self.token = token;
                    return;
                }
            }
        }
    }
}

#[derive(Default)]
struct TokenStreamBuilder {
    buf: Vec<TreeAndJoint>,
}

impl TokenStreamBuilder {
    fn push(&mut self, (tree, joint): TreeAndJoint) {
        if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
            if let TokenTree::Token(token) = &tree {
                if let Some(glued) = prev_token.glue(token) {
                    self.buf.pop();
                    self.buf.push((TokenTree::Token(glued), joint));
                    return;
                }
            }
        }
        self.buf.push((tree, joint))
    }

    fn into_token_stream(self) -> TokenStream {
        TokenStream::new(self.buf)
    }
}
<commit_msg>Dropping "block is empty..." error message on incorrect closing delimiter #70583<commit_after>use super::{StringReader, UnmatchedBrace};

use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::{
    DelimSpan,
    IsJoint::{self, *},
    TokenStream, TokenTree, TreeAndJoint,
};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
use rustc_span::Span;

impl<'a> StringReader<'a> {
    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
        let mut tt_reader = TokenTreesReader {
            string_reader: self,
            token: Token::dummy(),
            joint_to_prev: Joint,
            open_braces: Vec::new(),
            unmatched_braces: Vec::new(),
            matching_delim_spans: Vec::new(),
            last_unclosed_found_span: None,
            last_delim_empty_block_spans: FxHashMap::default(),
        };
        let res = tt_reader.parse_all_token_trees();
        (res, tt_reader.unmatched_braces)
    }
}

struct TokenTreesReader<'a> {
    string_reader: StringReader<'a>,
    token: Token,
    joint_to_prev: IsJoint,
    /// Stack of open delimiters and their spans. Used for error message.
    open_braces: Vec<(token::DelimToken, Span)>,
    unmatched_braces: Vec<UnmatchedBrace>,
    /// The type and spans for all braces
    ///
    /// Used only for error recovery when arriving to EOF with mismatched braces.
    matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
    last_unclosed_found_span: Option<Span>,
    /// Collect empty block spans that might have been auto-inserted by editors.
    last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
}

impl<'a> TokenTreesReader<'a> {
    // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
    fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
        let mut buf = TokenStreamBuilder::default();

        self.real_token();
        while self.token != token::Eof {
            buf.push(self.parse_token_tree()?);
        }

        Ok(buf.into_token_stream())
    }

    // Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
    fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
        let mut buf = TokenStreamBuilder::default();
        loop {
            if let token::CloseDelim(..) = self.token.kind {
                return buf.into_token_stream();
            }

            match self.parse_token_tree() {
                Ok(tree) => buf.push(tree),
                Err(mut e) => {
                    e.emit();
                    return buf.into_token_stream();
                }
            }
        }
    }

    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
        let sm = self.string_reader.sess.source_map();

        match self.token.kind {
            token::Eof => {
                let msg = "this file contains an unclosed delimiter";
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
                for &(_, sp) in &self.open_braces {
                    err.span_label(sp, "unclosed delimiter");
                    self.unmatched_braces.push(UnmatchedBrace {
                        expected_delim: token::DelimToken::Brace,
                        found_delim: None,
                        found_span: self.token.span,
                        unclosed_span: Some(sp),
                        candidate_span: None,
                    });
                }

                if let Some((delim, _)) = self.open_braces.last() {
                    if let Some((_, open_sp, close_sp)) =
                        self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
                            if let Some(close_padding) = sm.span_to_margin(*close_sp) {
                                if let Some(open_padding) = sm.span_to_margin(*open_sp) {
                                    return delim == d && close_padding != open_padding;
                                }
                            }
                            false
                        })
                    // these are in reverse order as they get inserted on close, but
                    {
                        // we want the last open/first close
                        err.span_label(*open_sp, "this delimiter might not be properly closed...");
                        err.span_label(
                            *close_sp,
                            "...as it matches this but it has different indentation",
                        );
                    }
                }
                Err(err)
            }
            token::OpenDelim(delim) => {
                // The span for beginning of the delimited section
                let pre_span = self.token.span;

                // Parse the open delimiter.
                self.open_braces.push((delim, self.token.span));
                self.real_token();

                // Parse the token trees within the delimiters.
                // We stop at any delimiter so we can try to recover if the user
                // uses an incorrect delimiter.
                let tts = self.parse_token_trees_until_close_delim();

                // Expand to cover the entire delimited token tree
                let delim_span = DelimSpan::from_pair(pre_span, self.token.span);

                match self.token.kind {
                    // Correct delimiter.
                    token::CloseDelim(d) if d == delim => {
                        let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
                        let close_brace_span = self.token.span;

                        if tts.is_empty() {
                            let empty_block_span = open_brace_span.to(close_brace_span);
                            if !sm.is_multiline(empty_block_span) {
                                // Only track if the block is in the form of `{}`, otherwise it is
                                // likely that it was written on purpose.
                                self.last_delim_empty_block_spans.insert(delim, empty_block_span);
                            }
                        }

                        if self.open_braces.is_empty() {
                            // Clear up these spans to avoid suggesting them as we've found
                            // properly matched delimiters so far for an entire block.
                            self.matching_delim_spans.clear();
                        } else {
                            self.matching_delim_spans.push((
                                open_brace,
                                open_brace_span,
                                close_brace_span,
                            ));
                        }
                        // Parse the closing delimiter.
                        self.real_token();
                    }
                    // Incorrect delimiter.
                    token::CloseDelim(other) => {
                        let mut unclosed_delimiter = None;
                        let mut candidate = None;
                        if self.last_unclosed_found_span != Some(self.token.span) {
                            // do not complain about the same unclosed delimiter multiple times
                            self.last_unclosed_found_span = Some(self.token.span);
                            // This is a conservative error: only report the last unclosed
                            // delimiter. The previous unclosed delimiters could actually be
                            // closed! The parser just hasn't gotten to them yet.
                            if let Some(&(_, sp)) = self.open_braces.last() {
                                unclosed_delimiter = Some(sp);
                            };
                            if let Some(current_padding) = sm.span_to_margin(self.token.span) {
                                for (brace, brace_span) in &self.open_braces {
                                    if let Some(padding) = sm.span_to_margin(*brace_span) {
                                        // high likelihood of these two corresponding
                                        if current_padding == padding && brace == &other {
                                            candidate = Some(*brace_span);
                                        }
                                    }
                                }
                            }
                            let (tok, _) = self.open_braces.pop().unwrap();
                            self.unmatched_braces.push(UnmatchedBrace {
                                expected_delim: tok,
                                found_delim: Some(other),
                                found_span: self.token.span,
                                unclosed_span: unclosed_delimiter,
                                candidate_span: candidate,
                            });
                        } else {
                            self.open_braces.pop();
                        }

                        // If the incorrect delimiter matches an earlier opening
                        // delimiter, then don't consume it (it can be used to
                        // close the earlier one). Otherwise, consume it.
                        // E.g., we try to recover from:
                        // fn foo() {
                        //     bar(baz(
                        // }  // Incorrect delimiter but matches the earlier `{`
                        if !self.open_braces.iter().any(|&(b, _)| b == other) {
                            self.real_token();
                        }
                    }
                    token::Eof => {
                        // Silently recover, the EOF token will be seen again
                        // and an error emitted then. Thus we don't pop from
                        // self.open_braces here.
                    }
                    _ => {}
                }

                Ok(TokenTree::Delimited(delim_span, delim, tts).into())
            }
            token::CloseDelim(_delim) => {
                // An unexpected closing delimiter (i.e., there is no
                // matching opening delimiter).
                let token_str = token_to_string(&self.token);
                let msg = format!("unexpected closing delimiter: `{}`", token_str);
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);

                err.span_label(self.token.span, "unexpected closing delimiter");

                Err(err)
            }
            _ => {
                let tt = TokenTree::Token(self.token.take());
                self.real_token();
                let is_joint = self.joint_to_prev == Joint && self.token.is_op();
                Ok((tt, if is_joint { Joint } else { NonJoint }))
            }
        }
    }

    fn real_token(&mut self) {
        self.joint_to_prev = Joint;
        loop {
            let token = self.string_reader.next_token();
            match token.kind {
                token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
                    self.joint_to_prev = NonJoint;
                }
                _ => {
                    self.token = token;
                    return;
                }
            }
        }
    }
}

#[derive(Default)]
struct TokenStreamBuilder {
    buf: Vec<TreeAndJoint>,
}

impl TokenStreamBuilder {
    fn push(&mut self, (tree, joint): TreeAndJoint) {
        if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
            if let TokenTree::Token(token) = &tree {
                if let Some(glued) = prev_token.glue(token) {
                    self.buf.pop();
                    self.buf.push((TokenTree::Token(glued), joint));
                    return;
                }
            }
        }
        self.buf.push((tree, joint))
    }

    fn into_token_stream(self) -> TokenStream {
        TokenStream::new(self.buf)
    }
}
<|endoftext|>


<commit_before>//! Decode Cryptographic Control (CC) packets.

use consts::CRYPTO_CONTROL_BYTES;
use util::slice_u16;

/// Buffer of bytes that represent a crypto control packet.
pub type Buf = [u8; CRYPTO_CONTROL_BYTES];

/// Information necessary to decrypt an encrypted message.
pub struct CryptoControlFields(Buf);

impl CryptoControlFields {
    /// Create a new `CryptoControlFields` decoder from the given bytes.
    pub fn new(buf: Buf) -> Self { CryptoControlFields(buf) }

    /// Initialization vector used internally by associated crypto algorithm.
    pub fn init(&self) -> &[u8] { &self.0[..9] }
    /// Type of crypto algorithm in use, if any.
    pub fn alg(&self) -> CryptoAlgorithm { CryptoAlgorithm::from_bits(self.0[9]) }
    /// Encryption key to use.
    pub fn key(&self) -> u16 { slice_u16(&self.0[10..]) }
}

/// Type of cryptographic algorithm.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CryptoAlgorithm {
    Accordion,
    BatonEven,
    Firefly,
    Mayfly,
    Saville,
    BatonOdd,
    Unencrypted,
    DES,
    TripleDES,
    AES,
    Other(u8),
}

impl CryptoAlgorithm {
    /// Parse the given 8 bits into a crypto algorithm.
    pub fn from_bits(bits: u8) -> CryptoAlgorithm {
        use self::CryptoAlgorithm::*;

        match bits {
            0x00 => Accordion,
            0x01 => BatonEven,
            0x02 => Firefly,
            0x03 => Mayfly,
            0x04 => Saville,
            0x41 => BatonOdd,
            0x80 => Unencrypted,
            0x81 => DES,
            0x83 => TripleDES,
            0x84 => AES,
            b => Other(b),
        }
    }
}

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

    #[test]
    fn test_cypto() {
        let c = CryptoControlFields::new([
            0, 0, 0, 1, 0, 0, 0, 2, 0,
            0b10000100,
            0xDE, 0xAD,
        ]);

        assert_eq!(c.init(), &[0,0,0,1,0,0,0,2,0]);
        assert_eq!(c.alg(), AES);
        assert_eq!(c.key(), 0xDEAD);
    }
}
<commit_msg>s/DES/Des/<commit_after>//! Decode Cryptographic Control (CC) packets.

use consts::CRYPTO_CONTROL_BYTES;
use util::slice_u16;

/// Buffer of bytes that represent a crypto control packet.
pub type Buf = [u8; CRYPTO_CONTROL_BYTES];

/// Information necessary to decrypt an encrypted message.
pub struct CryptoControlFields(Buf);

impl CryptoControlFields {
    /// Create a new `CryptoControlFields` decoder from the given bytes.
    pub fn new(buf: Buf) -> Self { CryptoControlFields(buf) }

    /// Initialization vector used internally by associated crypto algorithm.
    pub fn init(&self) -> &[u8] { &self.0[..9] }
    /// Type of crypto algorithm in use, if any.
    pub fn alg(&self) -> CryptoAlgorithm { CryptoAlgorithm::from_bits(self.0[9]) }
    /// Encryption key to use.
    pub fn key(&self) -> u16 { slice_u16(&self.0[10..]) }
}

/// Type of cryptographic algorithm.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CryptoAlgorithm {
    Accordion,
    BatonEven,
    Firefly,
    Mayfly,
    Saville,
    BatonOdd,
    Unencrypted,
    Des,
    TripleDES,
    AES,
    Other(u8),
}

impl CryptoAlgorithm {
    /// Parse the given 8 bits into a crypto algorithm.
    pub fn from_bits(bits: u8) -> CryptoAlgorithm {
        use self::CryptoAlgorithm::*;

        match bits {
            0x00 => Accordion,
            0x01 => BatonEven,
            0x02 => Firefly,
            0x03 => Mayfly,
            0x04 => Saville,
            0x41 => BatonOdd,
            0x80 => Unencrypted,
            0x81 => Des,
            0x83 => TripleDES,
            0x84 => AES,
            b => Other(b),
        }
    }
}

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

    #[test]
    fn test_cypto() {
        let c = CryptoControlFields::new([
            0, 0, 0, 1, 0, 0, 0, 2, 0,
            0b10000100,
            0xDE, 0xAD,
        ]);

        assert_eq!(c.init(), &[0,0,0,1,0,0,0,2,0]);
        assert_eq!(c.alg(), AES);
        assert_eq!(c.key(), 0xDEAD);
    }
}
<|endoftext|>