<commit_before>use rustc::ty::{Ty, TyCtxt};
use rustc::mir::*;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use smallvec::SmallVec;
use syntax_pos::{Span};

use std::fmt;
use std::ops::{Index, IndexMut};

use self::abs_domain::{AbstractElem, Lift};

mod abs_domain;

newtype_index! {
    pub struct MovePathIndex {
        DEBUG_FORMAT = "mp{}"
    }
}

newtype_index! {
    pub struct MoveOutIndex {
        DEBUG_FORMAT = "mo{}"
    }
}

newtype_index! {
    pub struct InitIndex {
        DEBUG_FORMAT = "in{}"
    }
}

impl MoveOutIndex {
    pub fn move_path_index(&self, move_data: &MoveData<'_>) -> MovePathIndex {
        move_data.moves[*self].path
    }
}

/// `MovePath` is a canonicalized representation of a path that is
/// moved or assigned to.
///
/// It follows a tree structure.
///
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
/// move *out* of the place `x.m`.
///
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
/// one of them will link to the other via the `next_sibling` field,
/// and the other will have no entry in its `next_sibling` field), and
/// they both have the MovePath representing `x` as their parent.
#[derive(Clone)]
pub struct MovePath<'tcx> {
    pub next_sibling: Option<MovePathIndex>,
    pub first_child: Option<MovePathIndex>,
    pub parent: Option<MovePathIndex>,
    pub place: Place<'tcx>,
}

impl<'tcx> MovePath<'tcx> {
    pub fn parents(
        &self,
        move_paths: &IndexVec<MovePathIndex, MovePath<'_>>,
    ) -> Vec<MovePathIndex> {
        let mut parents = Vec::new();

        let mut curr_parent = self.parent;
        while let Some(parent_mpi) = curr_parent {
            parents.push(parent_mpi);
            curr_parent = move_paths[parent_mpi].parent;
        }

        parents
    }
}

impl<'tcx> fmt::Debug for MovePath<'tcx> {
    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(w, "MovePath {{")?;
        if let Some(parent) = self.parent {
            write!(w, " parent: {:?},", parent)?;
        }
        if let Some(first_child) = self.first_child {
            write!(w, " first_child: {:?},", first_child)?;
        }
        if let Some(next_sibling) = self.next_sibling {
            write!(w, " next_sibling: {:?}", next_sibling)?;
        }
        write!(w, " place: {:?} }}", self.place)
    }
}

impl<'tcx> fmt::Display for MovePath<'tcx> {
    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(w, "{:?}", self.place)
    }
}

#[derive(Debug)]
pub struct MoveData<'tcx> {
    pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
    pub moves: IndexVec<MoveOutIndex, MoveOut>,
    /// Each Location `l` is mapped to the MoveOut's that are effects
    /// of executing the code at `l`. (There can be multiple MoveOut's
    /// for a given `l` because each MoveOut is associated with one
    /// particular path being moved.)
    pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
    pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
    pub rev_lookup: MovePathLookup,
    pub inits: IndexVec<InitIndex, Init>,
    /// Each Location `l` is mapped to the Inits that are effects
    /// of executing the code at `l`.
    pub init_loc_map: LocationMap<SmallVec<[InitIndex; 4]>>,
    pub init_path_map: IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
}

pub trait HasMoveData<'tcx> {
    fn move_data(&self) -> &MoveData<'tcx>;
}

#[derive(Debug)]
pub struct LocationMap<T> {
    /// Location-indexed (BasicBlock for outer index, index within BB
    /// for inner index) map.
    pub(crate) map: IndexVec<BasicBlock, Vec<T>>,
}

impl<T> Index<Location> for LocationMap<T> {
    type Output = T;
    fn index(&self, index: Location) -> &Self::Output {
        &self.map[index.block][index.statement_index]
    }
}

impl<T> IndexMut<Location> for LocationMap<T> {
    fn index_mut(&mut self, index: Location) -> &mut Self::Output {
        &mut self.map[index.block][index.statement_index]
    }
}

impl<T> LocationMap<T> where T: Default + Clone {
    fn new(mir: &Mir<'_>) -> Self {
        LocationMap {
            map: mir.basic_blocks().iter().map(|block| {
                vec![T::default(); block.statements.len()+1]
            }).collect()
        }
    }
}

/// `MoveOut` represents a point in a program that moves out of some
/// L-value; i.e., "creates" uninitialized memory.
///
/// With respect to dataflow analysis:
/// - Generated by moves and declaration of uninitialized variables.
/// - Killed by assignments to the memory.
#[derive(Copy, Clone)]
pub struct MoveOut {
    /// path being moved
    pub path: MovePathIndex,
    /// location of move
    pub source: Location,
}

impl fmt::Debug for MoveOut {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{:?}@{:?}", self.path, self.source)
    }
}

/// `Init` represents a point in a program that initializes some L-value;
#[derive(Copy, Clone)]
pub struct Init {
    /// path being initialized
    pub path: MovePathIndex,
    /// location of initialization
    pub location: InitLocation,
    /// Extra information about this initialization
    pub kind: InitKind,
}

/// Initializations can be from an argument or from a statement. Arguments
/// do not have locations, in those cases the `Local` is kept..
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitLocation {
    Argument(Local),
    Statement(Location),
}

/// Additional information about the initialization.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitKind {
    /// Deep init, even on panic
    Deep,
    /// Only does a shallow init
    Shallow,
    /// This doesn't initialize the variable on panic (and a panic is possible).
    NonPanicPathOnly,
}

impl fmt::Debug for Init {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind)
    }
}

impl Init {
    crate fn span<'gcx>(&self, mir: &Mir<'gcx>) -> Span {
        match self.location {
            InitLocation::Argument(local) => mir.local_decls[local].source_info.span,
            InitLocation::Statement(location) => mir.source_info(location).span,
        }
    }
}

/// Tables mapping from a place to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup {
    locals: IndexVec<Local, MovePathIndex>,

    /// projections are made from a base-place and a projection
    /// elem. The base-place will have a unique MovePathIndex; we use
    /// the latter as the index into the outer vector (narrowing
    /// subsequent search so that it is solely relative to that
    /// base-place). For the remaining lookup, we map the projection
    /// elem to the associated MovePathIndex.
    projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>
}

mod builder;

#[derive(Copy, Clone, Debug)]
pub enum LookupResult {
    Exact(MovePathIndex),
    Parent(Option<MovePathIndex>)
}

impl MovePathLookup {
    // Unlike the builder `fn move_path_for` below, this lookup
    // alternative will *not* create a MovePath on the fly for an
    // unknown place, but will rather return the nearest available
    // parent.
    pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
        match *place {
            Place::Base(PlaceBase::Local(local)) => LookupResult::Exact(self.locals[local]),
            Place::Base(PlaceBase::Static(..)) => LookupResult::Parent(None),
            Place::Projection(ref proj) => {
                match self.find(&proj.base) {
                    LookupResult::Exact(base_path) => {
                        match self.projections.get(&(base_path, proj.elem.lift())) {
                            Some(&subpath) => LookupResult::Exact(subpath),
                            None => LookupResult::Parent(Some(base_path))
                        }
                    }
                    inexact => inexact
                }
            }
        }
    }

    pub fn find_local(&self, local: Local) -> MovePathIndex {
        self.locals[local]
    }
}

#[derive(Debug)]
pub struct IllegalMoveOrigin<'tcx> {
    pub(crate) location: Location,
    pub(crate) kind: IllegalMoveOriginKind<'tcx>,
}

#[derive(Debug)]
pub(crate) enum IllegalMoveOriginKind<'tcx> {
    /// Illegal move due to attempt to move from `static` variable.
    Static,

    /// Illegal move due to attempt to move from behind a reference.
    BorrowedContent {
        /// The place the reference refers to: if erroneous code was trying to
        /// move from `(*x).f` this will be `*x`.
        target_place: Place<'tcx>,
    },

    /// Illegal move due to attempt to move from field of an ADT that
    /// implements `Drop`. Rust maintains invariant that all `Drop`
    /// ADT's remain fully-initialized so that user-defined destructor
    /// can safely read from all of the ADT's fields.
    InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },

    /// Illegal move due to attempt to move out of a slice or array.
    InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool, },
}

#[derive(Debug)]
pub enum MoveError<'tcx> {
    IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
    UnionMove { path: MovePathIndex },
}

impl<'tcx> MoveError<'tcx> {
    fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
        let origin = IllegalMoveOrigin { location, kind };
        MoveError::IllegalMove { cannot_move_out_of: origin }
    }
}

impl<'a, 'gcx, 'tcx> MoveData<'tcx> {
    pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>)
                        -> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
        builder::gather_moves(mir, tcx)
    }

    /// For the move path `mpi`, returns the root local variable (if any) that starts the path.
    /// (e.g., for a path like `a.b.c` returns `Some(a)`)
    pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
        loop {
            let path = &self.move_paths[mpi];
            if let Place::Base(PlaceBase::Local(l)) = path.place { return Some(l); }
            if let Some(parent) = path.parent { mpi = parent; continue } else { return None }
        }
    }
}
<commit_msg>Rollup merge of #61103 - spastorino:find-iterate, r=oli-obk<commit_after>use rustc::ty::{Ty, TyCtxt};
use rustc::mir::*;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use smallvec::SmallVec;
use syntax_pos::{Span};

use std::fmt;
use std::ops::{Index, IndexMut};

use self::abs_domain::{AbstractElem, Lift};

mod abs_domain;

newtype_index! {
    pub struct MovePathIndex {
        DEBUG_FORMAT = "mp{}"
    }
}

newtype_index! {
    pub struct MoveOutIndex {
        DEBUG_FORMAT = "mo{}"
    }
}

newtype_index! {
    pub struct InitIndex {
        DEBUG_FORMAT = "in{}"
    }
}

impl MoveOutIndex {
    pub fn move_path_index(&self, move_data: &MoveData<'_>) -> MovePathIndex {
        move_data.moves[*self].path
    }
}

/// `MovePath` is a canonicalized representation of a path that is
/// moved or assigned to.
///
/// It follows a tree structure.
///
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
/// move *out* of the place `x.m`.
///
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
/// one of them will link to the other via the `next_sibling` field,
/// and the other will have no entry in its `next_sibling` field), and
/// they both have the MovePath representing `x` as their parent.
#[derive(Clone)]
pub struct MovePath<'tcx> {
    pub next_sibling: Option<MovePathIndex>,
    pub first_child: Option<MovePathIndex>,
    pub parent: Option<MovePathIndex>,
    pub place: Place<'tcx>,
}

impl<'tcx> MovePath<'tcx> {
    pub fn parents(
        &self,
        move_paths: &IndexVec<MovePathIndex, MovePath<'_>>,
    ) -> Vec<MovePathIndex> {
        let mut parents = Vec::new();

        let mut curr_parent = self.parent;
        while let Some(parent_mpi) = curr_parent {
            parents.push(parent_mpi);
            curr_parent = move_paths[parent_mpi].parent;
        }

        parents
    }
}

impl<'tcx> fmt::Debug for MovePath<'tcx> {
    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(w, "MovePath {{")?;
        if let Some(parent) = self.parent {
            write!(w, " parent: {:?},", parent)?;
        }
        if let Some(first_child) = self.first_child {
            write!(w, " first_child: {:?},", first_child)?;
        }
        if let Some(next_sibling) = self.next_sibling {
            write!(w, " next_sibling: {:?}", next_sibling)?;
        }
        write!(w, " place: {:?} }}", self.place)
    }
}

impl<'tcx> fmt::Display for MovePath<'tcx> {
    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(w, "{:?}", self.place)
    }
}

#[derive(Debug)]
pub struct MoveData<'tcx> {
    pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
    pub moves: IndexVec<MoveOutIndex, MoveOut>,
    /// Each Location `l` is mapped to the MoveOut's that are effects
    /// of executing the code at `l`. (There can be multiple MoveOut's
    /// for a given `l` because each MoveOut is associated with one
    /// particular path being moved.)
    pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
    pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
    pub rev_lookup: MovePathLookup,
    pub inits: IndexVec<InitIndex, Init>,
    /// Each Location `l` is mapped to the Inits that are effects
    /// of executing the code at `l`.
    pub init_loc_map: LocationMap<SmallVec<[InitIndex; 4]>>,
    pub init_path_map: IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
}

pub trait HasMoveData<'tcx> {
    fn move_data(&self) -> &MoveData<'tcx>;
}

#[derive(Debug)]
pub struct LocationMap<T> {
    /// Location-indexed (BasicBlock for outer index, index within BB
    /// for inner index) map.
    pub(crate) map: IndexVec<BasicBlock, Vec<T>>,
}

impl<T> Index<Location> for LocationMap<T> {
    type Output = T;
    fn index(&self, index: Location) -> &Self::Output {
        &self.map[index.block][index.statement_index]
    }
}

impl<T> IndexMut<Location> for LocationMap<T> {
    fn index_mut(&mut self, index: Location) -> &mut Self::Output {
        &mut self.map[index.block][index.statement_index]
    }
}

impl<T> LocationMap<T> where T: Default + Clone {
    fn new(mir: &Mir<'_>) -> Self {
        LocationMap {
            map: mir.basic_blocks().iter().map(|block| {
                vec![T::default(); block.statements.len()+1]
            }).collect()
        }
    }
}

/// `MoveOut` represents a point in a program that moves out of some
/// L-value; i.e., "creates" uninitialized memory.
///
/// With respect to dataflow analysis:
/// - Generated by moves and declaration of uninitialized variables.
/// - Killed by assignments to the memory.
#[derive(Copy, Clone)]
pub struct MoveOut {
    /// path being moved
    pub path: MovePathIndex,
    /// location of move
    pub source: Location,
}

impl fmt::Debug for MoveOut {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{:?}@{:?}", self.path, self.source)
    }
}

/// `Init` represents a point in a program that initializes some L-value;
#[derive(Copy, Clone)]
pub struct Init {
    /// path being initialized
    pub path: MovePathIndex,
    /// location of initialization
    pub location: InitLocation,
    /// Extra information about this initialization
    pub kind: InitKind,
}

/// Initializations can be from an argument or from a statement. Arguments
/// do not have locations, in those cases the `Local` is kept..
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitLocation {
    Argument(Local),
    Statement(Location),
}

/// Additional information about the initialization.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitKind {
    /// Deep init, even on panic
    Deep,
    /// Only does a shallow init
    Shallow,
    /// This doesn't initialize the variable on panic (and a panic is possible).
    NonPanicPathOnly,
}

impl fmt::Debug for Init {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind)
    }
}

impl Init {
    crate fn span<'gcx>(&self, mir: &Mir<'gcx>) -> Span {
        match self.location {
            InitLocation::Argument(local) => mir.local_decls[local].source_info.span,
            InitLocation::Statement(location) => mir.source_info(location).span,
        }
    }
}

/// Tables mapping from a place to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup {
    locals: IndexVec<Local, MovePathIndex>,

    /// projections are made from a base-place and a projection
    /// elem. The base-place will have a unique MovePathIndex; we use
    /// the latter as the index into the outer vector (narrowing
    /// subsequent search so that it is solely relative to that
    /// base-place). For the remaining lookup, we map the projection
    /// elem to the associated MovePathIndex.
    projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>
}

mod builder;

#[derive(Copy, Clone, Debug)]
pub enum LookupResult {
    Exact(MovePathIndex),
    Parent(Option<MovePathIndex>)
}

impl MovePathLookup {
    // Unlike the builder `fn move_path_for` below, this lookup
    // alternative will *not* create a MovePath on the fly for an
    // unknown place, but will rather return the nearest available
    // parent.
    pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
        place.iterate(|place_base, place_projection| {
            let mut result = match place_base {
                PlaceBase::Local(local) => self.locals[*local],
                PlaceBase::Static(..) => return LookupResult::Parent(None),
            };

            for proj in place_projection {
                if let Some(&subpath) = self.projections.get(&(result, proj.elem.lift())) {
                    result = subpath;
                } else {
                    return LookupResult::Parent(Some(result));
                }
            }

            LookupResult::Exact(result)
        })
    }

    pub fn find_local(&self, local: Local) -> MovePathIndex {
        self.locals[local]
    }
}

#[derive(Debug)]
pub struct IllegalMoveOrigin<'tcx> {
    pub(crate) location: Location,
    pub(crate) kind: IllegalMoveOriginKind<'tcx>,
}

#[derive(Debug)]
pub(crate) enum IllegalMoveOriginKind<'tcx> {
    /// Illegal move due to attempt to move from `static` variable.
    Static,

    /// Illegal move due to attempt to move from behind a reference.
    BorrowedContent {
        /// The place the reference refers to: if erroneous code was trying to
        /// move from `(*x).f` this will be `*x`.
        target_place: Place<'tcx>,
    },

    /// Illegal move due to attempt to move from field of an ADT that
    /// implements `Drop`. Rust maintains invariant that all `Drop`
    /// ADT's remain fully-initialized so that user-defined destructor
    /// can safely read from all of the ADT's fields.
    InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },

    /// Illegal move due to attempt to move out of a slice or array.
    InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool, },
}

#[derive(Debug)]
pub enum MoveError<'tcx> {
    IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
    UnionMove { path: MovePathIndex },
}

impl<'tcx> MoveError<'tcx> {
    fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
        let origin = IllegalMoveOrigin { location, kind };
        MoveError::IllegalMove { cannot_move_out_of: origin }
    }
}

impl<'a, 'gcx, 'tcx> MoveData<'tcx> {
    pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>)
                        -> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
        builder::gather_moves(mir, tcx)
    }

    /// For the move path `mpi`, returns the root local variable (if any) that starts the path.
    /// (e.g., for a path like `a.b.c` returns `Some(a)`)
    pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
        loop {
            let path = &self.move_paths[mpi];
            if let Place::Base(PlaceBase::Local(l)) = path.place { return Some(l); }
            if let Some(parent) = path.parent { mpi = parent; continue } else { return None }
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::process;
use std::process::{Command, Stdio};
use sys_info;
use kuchiki;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::Client;
use config::Config;
use extractor;
use downloader;
use profile;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    fs::create_dir_all(&config.versions_dir).expect("Could not create versions directory.");
    if !env::home_dir()
        .unwrap()
        .join(".profile")
        .exists() {
        println!(r#"Write the following thing:

. $HOME\.groonga\shims\bin\source-groonga.sh
"#)
    }
}

pub fn install(m: &ArgMatches) {
    const BASE_URL: &'static str = "http://packages.groonga.org/source/groonga";
    let config = Config::from_matches(m);
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    let groonga_source = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = Client::new();
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_source),
                                             download_dir.clone(),
                                             "groonga.zip")
        .expect("Failed to download");
    extractor::extract_zip(&filename, &download_dir);
    assert!(env::set_current_dir(&download_dir.join(groonga_dir.clone())).is_ok());

    // TODO: Should specify on Linux?
    match inner_autoreconf() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not execute autoreconf -v.");
            process::exit(1);
        }
    }

    match inner_configure(&config, groonga_dir.clone()) {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not configure.");
            process::exit(1);
        }
    }

    match inner_build() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not build.");
            process::exit(1);
        }
    }

    match inner_install() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not install.");
            process::exit(1);
        }
    }

    #[cfg(target_os = "macos")]
    fn inner_autoreconf() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("autoreconf")
            .args(&["-v"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    #[cfg(not(target_os = "macos"))]
    fn inner_autoreconf() -> Result<u32, io::Error> {
        Ok(0)
    }

    // mainly used for macOS.
    fn openssl_pkg_config_path() -> String {
        let key = "OPENSSL_PKG_CONFIG_PATH";
        env::var(key).unwrap_or("/usr/local/opt/openssl/lib/pkgconfig".to_string())
    }

    fn inner_configure(config: &Config,
                       groonga_dir: String)
                       -> Result<process::ExitStatus, io::Error> {
        println!("{}",
                 config.versions_dir.join(groonga_dir.clone()).display());
        let mut cmd = Command::new("./configure")
            .args(&[&*format!("--prefix={}",
                              config.versions_dir.join(groonga_dir.clone()).display()),
                    &*format!("PKG_CONFIG_PATH={}", openssl_pkg_config_path())])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_build() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["-j", &*format!("{}", sys_info::cpu_num().unwrap_or(2))])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_install() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["install"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    match config.version {
        Some("system") => {
            profile::unix::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::unix::create_profile_source(&config.shim_dir,
                                                 &groonga_dir,
                                                 &config.versions_dir)
                .expect("Could not create source-groonga.sh")
        }
        None => unreachable!(),
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    } else {
        println!("{} is not installed!", groonga_dir.clone());
        process::exit(1);
    }
}

pub fn list() {
    use kuchiki::traits::*;

    if let Ok(doc) = kuchiki::parse_html().from_http("http://packages.groonga.org/source/groonga") {
        let docs = doc.select("tr").unwrap().collect::<Vec<_>>();
        println!("Installable Groonga:");
        for handle in &docs {
            let texts = handle.as_node().descendants().text_nodes().collect::<Vec<_>>();
            if let Some(text) = texts.first() {
                let package = text.as_node().text_contents();
                if package.contains("groonga") && package.contains("zip") {
                    let display = package.split(".zip").collect::<Vec<_>>();
                    println!("\t{}", display.first().unwrap_or(&""));
                }
            }
        }
    } else {
        println!("{}", "The page couldn't be fetched");
    }
}
<commit_msg>unix subcommand list: Tidy up outputs<commit_after>use std::env;
use std::fs;
use std::io;
use std::process;
use std::process::{Command, Stdio};
use sys_info;
use kuchiki;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::Client;
use config::Config;
use extractor;
use downloader;
use profile;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    fs::create_dir_all(&config.versions_dir).expect("Could not create versions directory.");
    if !env::home_dir()
        .unwrap()
        .join(".profile")
        .exists() {
        println!(r#"Write the following thing:

. $HOME\.groonga\shims\bin\source-groonga.sh
"#)
    }
}

pub fn install(m: &ArgMatches) {
    const BASE_URL: &'static str = "http://packages.groonga.org/source/groonga";
    let config = Config::from_matches(m);
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    let groonga_source = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = Client::new();
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_source),
                                             download_dir.clone(),
                                             "groonga.zip")
        .expect("Failed to download");
    extractor::extract_zip(&filename, &download_dir);
    assert!(env::set_current_dir(&download_dir.join(groonga_dir.clone())).is_ok());

    // TODO: Should specify on Linux?
    match inner_autoreconf() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not execute autoreconf -v.");
            process::exit(1);
        }
    }

    match inner_configure(&config, groonga_dir.clone()) {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not configure.");
            process::exit(1);
        }
    }

    match inner_build() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not build.");
            process::exit(1);
        }
    }

    match inner_install() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not install.");
            process::exit(1);
        }
    }

    #[cfg(target_os = "macos")]
    fn inner_autoreconf() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("autoreconf")
            .args(&["-v"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    #[cfg(not(target_os = "macos"))]
    fn inner_autoreconf() -> Result<u32, io::Error> {
        Ok(0)
    }

    // mainly used for macOS.
    fn openssl_pkg_config_path() -> String {
        let key = "OPENSSL_PKG_CONFIG_PATH";
        env::var(key).unwrap_or("/usr/local/opt/openssl/lib/pkgconfig".to_string())
    }

    fn inner_configure(config: &Config,
                       groonga_dir: String)
                       -> Result<process::ExitStatus, io::Error> {
        println!("{}",
                 config.versions_dir.join(groonga_dir.clone()).display());
        let mut cmd = Command::new("./configure")
            .args(&[&*format!("--prefix={}",
                              config.versions_dir.join(groonga_dir.clone()).display()),
                    &*format!("PKG_CONFIG_PATH={}", openssl_pkg_config_path())])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_build() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["-j", &*format!("{}", sys_info::cpu_num().unwrap_or(2))])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_install() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["install"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    match config.version {
        Some("system") => {
            profile::unix::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::unix::create_profile_source(&config.shim_dir,
                                                 &groonga_dir,
                                                 &config.versions_dir)
                .expect("Could not create source-groonga.sh")
        }
        None => unreachable!(),
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    } else {
        println!("{} is not installed!", groonga_dir.clone());
        process::exit(1);
    }
}

pub fn list() {
    use kuchiki::traits::*;

    if let Ok(doc) = kuchiki::parse_html().from_http("http://packages.groonga.org/source/groonga") {
        let docs = doc.select("tr").unwrap_or_else(|e| panic!("failed to find tr elements: {:?}", e)).collect::<Vec<_>>();
        println!("Installable Groonga:");
        for handle in &docs {
            let texts = handle.as_node().descendants().text_nodes().collect::<Vec<_>>();
            if let Some(text) = texts.first() {
                let package = text.as_node().text_contents();
                if package.contains("groonga") && package.contains("zip") && !package.contains("asc") {
                    let display = package.split(".zip").collect::<Vec<_>>();
                    println!("\t{}", display.first().unwrap_or(&""));
                }
            }
        }
    } else {
        println!("{}", "The page couldn't be fetched");
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::process;
use std::process::{Command, Stdio};
use sys_info;
use kuchiki;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::{Client, Url};
use config::Config;
use extractor;
use downloader;
use profile;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    fs::create_dir_all(&config.versions_dir).expect("Could not create versions directory.");
    if !env::home_dir()
        .unwrap_or_else(|| panic!("Cound not found homedir."))
        .join(".profile")
        .exists() {
        println!(r#"Write the following thing:

. $HOME\.groonga\shims\bin\source-groonga.sh
"#)
    }
}

pub fn install(m: &ArgMatches) {
    extern crate env_proxy;

    const BASE_URL: &'static str = "http://packages.groonga.org/source/groonga";
    let maybe_proxy = env_proxy::for_url(&Url::parse(BASE_URL).unwrap());
    let config = Config::from_matches(m);
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    let groonga_source = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = match maybe_proxy {
        None => Client::new(),
        Some(host_port) => Client::with_http_proxy(host_port.0, host_port.1),
    };
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_source),
                                             download_dir.clone(),
                                             "groonga.zip")
        .expect("Failed to download");
    extractor::extract_zip(&filename, &download_dir);
    assert!(env::set_current_dir(&download_dir.join(groonga_dir.clone())).is_ok());

    // TODO: Should specify on Linux?
    match inner_autoreconf() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not execute autoreconf -v.");
            process::exit(1);
        }
    }

    match inner_configure(&config, groonga_dir.clone()) {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not configure.");
            process::exit(1);
        }
    }

    match inner_build() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not build.");
            process::exit(1);
        }
    }

    match inner_install() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not install.");
            process::exit(1);
        }
    }

    #[cfg(target_os = "macos")]
    fn inner_autoreconf() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("autoreconf")
            .args(&["-v"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    #[cfg(not(target_os = "macos"))]
    fn inner_autoreconf() -> Result<u32, io::Error> {
        Ok(0)
    }

    // mainly used for macOS.
    fn openssl_pkg_config_path() -> String {
        let key = "OPENSSL_PKG_CONFIG_PATH";
        env::var(key).unwrap_or("/usr/local/opt/openssl/lib/pkgconfig".to_string())
    }

    fn inner_configure(config: &Config,
                       groonga_dir: String)
                       -> Result<process::ExitStatus, io::Error> {
        println!("{}",
                 config.versions_dir.join(groonga_dir.clone()).display());
        let mut cmd = Command::new("./configure")
            .args(&[&*format!("--prefix={}",
                              config.versions_dir.join(groonga_dir.clone()).display()),
                    &*format!("PKG_CONFIG_PATH={}", openssl_pkg_config_path())])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_build() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["-j", &*format!("{}", sys_info::cpu_num().unwrap_or(2))])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_install() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["install"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    match config.version {
        Some("system") => {
            profile::unix::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::unix::create_profile_source(&config.shim_dir,
                                                 &groonga_dir,
                                                 &config.versions_dir)
                .expect("Could not create source-groonga.sh")
        }
        None => unreachable!(),
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    } else {
        println!("{} is not installed!", groonga_dir.clone());
        process::exit(1);
    }
}

pub fn list() {
    use kuchiki::traits::*;

    if let Ok(doc) = kuchiki::parse_html().from_http("http://packages.groonga.org/source/groonga") {
        let docs = doc.select("tr")
            .unwrap_or_else(|e| panic!("failed to find tr elements: {:?}", e))
            .collect::<Vec<_>>();
        println!("Installable Groonga:");
        for handle in &docs {
            let texts = handle.as_node().descendants().text_nodes().collect::<Vec<_>>();
            if let Some(text) = texts.first() {
                let package = text.as_node().text_contents();
                if package.contains("groonga") && package.contains("zip") &&
                   !package.contains("asc") {
                    let package = package.split(".zip").collect::<Vec<_>>();
                    let pkg =
                        package.first().unwrap_or(&"").to_owned().split("-").collect::<Vec<_>>();
                    println!("\t{}", pkg.get(1).unwrap_or(&""));
                }
            }
        }
    } else {
        println!("{}", "The page couldn't be fetched");
    }
}
<commit_msg>unix list: Support proxy<commit_after>use std::env;
use std::fs;
use std::io;
use std::process;
use std::process::{Command, Stdio};
use sys_info;
use kuchiki;

use clap::ArgMatches;
use tempdir::TempDir;
use hyper::{Client, Url};
use config::Config;
use extractor;
use downloader;
use profile;

pub fn init() {
    let config = Config::new();
    if config.install_dir.exists() || config.shim_dir.exists() {
        println!("Already initalized. Reinitializing....");
    }
    fs::create_dir_all(&config.install_dir).expect("Could not create installation directory.");
    fs::create_dir_all(&config.shim_dir).expect("Could not create shims directory.");
    fs::create_dir_all(&config.versions_dir).expect("Could not create versions directory.");
    if !env::home_dir()
        .unwrap_or_else(|| panic!("Cound not found homedir."))
        .join(".profile")
        .exists() {
        println!(r#"Write the following thing:

. $HOME\.groonga\shims\bin\source-groonga.sh
"#)
    }
}

pub fn install(m: &ArgMatches) {
    extern crate env_proxy;

    const BASE_URL: &'static str = "http://packages.groonga.org/source/groonga";
    let maybe_proxy = env_proxy::for_url(&Url::parse(BASE_URL).unwrap());
    let config = Config::from_matches(m);
    println!("Obtaining Groonga version: {}",
             config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    let groonga_source = format!("{}.zip", groonga_dir.clone());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Already installed. Ready to use it.");
        return ();
    }

    let download_dir = TempDir::new("grnenv-rs")
        .expect("Could not create temp dir.")
        .into_path();

    let client = match maybe_proxy {
        None => Client::new(),
        Some(host_port) => Client::with_http_proxy(host_port.0, host_port.1),
    };
    let filename = downloader::file_download(&client,
                                             &*format!("{}/{}", BASE_URL, groonga_source),
                                             download_dir.clone(),
                                             "groonga.zip")
        .expect("Failed to download");
    extractor::extract_zip(&filename, &download_dir);
    assert!(env::set_current_dir(&download_dir.join(groonga_dir.clone())).is_ok());

    // TODO: Should specify on Linux?
    match inner_autoreconf() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not execute autoreconf -v.");
            process::exit(1);
        }
    }

    match inner_configure(&config, groonga_dir.clone()) {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not configure.");
            process::exit(1);
        }
    }

    match inner_build() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not build.");
            process::exit(1);
        }
    }

    match inner_install() {
        Ok(o) => println!("{}", o),
        Err(_) => {
            println!("Could not install.");
            process::exit(1);
        }
    }

    #[cfg(target_os = "macos")]
    fn inner_autoreconf() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("autoreconf")
            .args(&["-v"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    #[cfg(not(target_os = "macos"))]
    fn inner_autoreconf() -> Result<u32, io::Error> {
        Ok(0)
    }

    // mainly used for macOS.
    fn openssl_pkg_config_path() -> String {
        let key = "OPENSSL_PKG_CONFIG_PATH";
        env::var(key).unwrap_or("/usr/local/opt/openssl/lib/pkgconfig".to_string())
    }

    fn inner_configure(config: &Config,
                       groonga_dir: String)
                       -> Result<process::ExitStatus, io::Error> {
        println!("{}",
                 config.versions_dir.join(groonga_dir.clone()).display());
        let mut cmd = Command::new("./configure")
            .args(&[&*format!("--prefix={}",
                              config.versions_dir.join(groonga_dir.clone()).display()),
                    &*format!("PKG_CONFIG_PATH={}", openssl_pkg_config_path())])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_build() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["-j", &*format!("{}", sys_info::cpu_num().unwrap_or(2))])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }

    fn inner_install() -> Result<process::ExitStatus, io::Error> {
        let mut cmd = Command::new("make")
            .args(&["install"])
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute child: {}", e));
        let status = cmd.wait();
        status
    }
}

pub fn switch(m: &ArgMatches) {
    let config = Config::from_matches(m);
    println!("Using Groonga version: {}", config.version.clone().unwrap());
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    match config.version {
        Some("system") => {
            profile::unix::remove_grnenv_profile(&config.shim_dir).unwrap();
            return ();
        }
        Some(_) => {
            profile::unix::create_profile_source(&config.shim_dir,
                                                 &groonga_dir,
                                                 &config.versions_dir)
                .expect("Could not create source-groonga.sh")
        }
        None => unreachable!(),
    }
}

pub fn uninstall(m: &ArgMatches) {
    let config = Config::from_matches(m);
    let groonga_dir = format!("groonga-{}", config.version.unwrap());
    if config.versions_dir.join(groonga_dir.clone()).exists() {
        println!("Removing {}....", groonga_dir.clone());
        fs::remove_dir_all(&config.versions_dir.join(groonga_dir))
            .expect("Could not remove specified directory.");
    } else {
        println!("{} is not installed!", groonga_dir.clone());
        process::exit(1);
    }
}

pub fn list() {
    use kuchiki::traits::*;
    use command::common::MaybeProxyUrl;

    let base_url: &'static str = "http://packages.groonga.org/source/groonga";
    let maybe_proxy_url = MaybeProxyUrl {
        url: Url::parse(base_url).unwrap()
    };
    if let Ok(doc) = kuchiki::parse_html().from_http(maybe_proxy_url) {
        let docs = doc.select("tr")
            .unwrap_or_else(|e| panic!("failed to find tr elements: {:?}", e))
            .collect::<Vec<_>>();
        println!("Installable Groonga:");
        for handle in &docs {
            let texts = handle.as_node().descendants().text_nodes().collect::<Vec<_>>();
            if let Some(text) = texts.first() {
                let package = text.as_node().text_contents();
                if package.contains("groonga") && package.contains("zip") &&
                   !package.contains("asc") {
                    let package = package.split(".zip").collect::<Vec<_>>();
                    let pkg =
                        package.first().unwrap_or(&"").to_owned().split("-").collect::<Vec<_>>();
                    println!("\t{}", pkg.get(1).unwrap_or(&""));
                }
            }
        }
    } else {
        println!("{}", "The page couldn't be fetched");
    }
}
<|endoftext|>


<commit_before>use command::{Command, Query};
use commandlinable::Escape;
use std::borrow::Cow;

#[derive(Clone, Debug)]
pub struct CommandLine {
    command: Command,
    arguments: Query,
}

impl CommandLine {
    pub fn new(command: Command, query: Query) -> CommandLine {
        CommandLine {
            command: command,
            arguments: query,
        }
    }

    /// Get command enum constant to construct command line query.
    pub fn get_command(&mut self) -> Cow<Command> {
        Cow::Borrowed(&self.command)
    }

    /// Get vectorize `("key", "value")` pairs to construct command line query.
    pub fn get_argument(&mut self) -> Cow<Query> {
        Cow::Borrowed(&self.arguments)
    }

    /// Set vectorize `("key", "value")` pairs to construct command line query.
    pub fn set_argument(&mut self, arguments: Query) {
        self.arguments = arguments
    }

    /// Create command line style command query.
    ///
    /// `vec![("key","value")]` interprets to `"--key value"`.
    /// And two or more value pair are concatinate with ` `(a space).
    pub fn make_command<'a>(&mut self) -> Cow<'a, str> {
        let mut output = String::new();
        for query in self.arguments.to_owned() {
            let (key, value) = query;
            if output.len() >= 1 {
                output.push_str(" ");
            }
            let format_string = format!("--{} {}", &*key, Escape(&*value));
            output.push_str(format_string.as_str());
        }
        Cow::Owned(output)
    }

    ///
    /// Create Groonga command line style command.
    pub fn encode(&mut self) -> String {
        format!("{} {}", self.get_command().into_owned(), self.make_command().into_owned())
            .trim()
            .to_string()
    }
}

#[cfg(test)]
mod test {
    use super::CommandLine;
    use select::SelectCommand;
    use load::{LoadCommand, InputType};
    use status::StatusCommand;
    use dump::DumpCommand;
    use delete::DeleteCommand;
    use table_create::TableCreateCommand;
    use types::table_flag_type::TableFlagType;
    use column_create::ColumnCreateCommand;
    use types::data_type::DataType;

    #[test]
    fn test_select() {
        let (command, query) = SelectCommand::new("Test".to_string())
            .filter("output_column @ \"type_safe\"".to_string())
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "select --table Test --filter \'output_column @ \"type_safe\"\'";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_load() {
        let load_data: &'static str = r#"[
{"_key":"http://example.org/","title":"This is test record 1!"},
]"#;
        let (command, query, values) = LoadCommand::new("Test".to_string(), load_data.to_string())
            .input_type(InputType::Json)
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "load --table Test --input_type json";
        assert_eq!(cli_encoded.to_string(), command_line);
        assert_eq!(load_data.to_string(), values);
    }

    #[test]
    fn test_status() {
        let (command, query) = StatusCommand::new().build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "status";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_dump() {
        let (command, query) = DumpCommand::new()
            .tables(vec!["Books".to_string(), "Categories".to_string()])
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "dump --tables Books,Categories";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_delete() {
        let (command, query) = DeleteCommand::new("Books".to_string())
            .filter("author == unknown".to_string())
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "delete --table Books --filter \'author == unknown\'";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_table_create() {
        let (command, query) = TableCreateCommand::new("Test".to_string())
            .flags(vec![(TableFlagType::PatKey), (TableFlagType::KeyWithSIS)])
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let url_encoded = "table_create --name Test --flags TABLE_PAT_KEY|KEY_WITH_SIS";
        assert_eq!(url_encoded.to_string(), command_line);
    }

    #[test]
    fn test_column_create() {
        let (command, query) = ColumnCreateCommand::new("Test".to_string(), "element".to_string())
            .column_type(DataType::LongText)
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "column_create --table Test --name element --type LongText";
        assert_eq!(cli_encoded.to_string(), command_line);
    }
}
<commit_msg>rustfmt<commit_after>use command::{Command, Query};
use commandlinable::Escape;
use std::borrow::Cow;

#[derive(Clone, Debug)]
pub struct CommandLine {
    command: Command,
    arguments: Query,
}

impl CommandLine {
    pub fn new(command: Command, query: Query) -> CommandLine {
        CommandLine {
            command: command,
            arguments: query,
        }
    }

    /// Get command enum constant to construct command line query.
    pub fn get_command(&mut self) -> Cow<Command> {
        Cow::Borrowed(&self.command)
    }

    /// Get vectorize `("key", "value")` pairs to construct command line query.
    pub fn get_argument(&mut self) -> Cow<Query> {
        Cow::Borrowed(&self.arguments)
    }

    /// Set vectorize `("key", "value")` pairs to construct command line query.
    pub fn set_argument(&mut self, arguments: Query) {
        self.arguments = arguments
    }

    /// Create command line style command query.
    ///
    /// `vec![("key","value")]` interprets to `"--key value"`.
    /// And two or more value pair are concatinate with ` `(a space).
    pub fn make_command<'a>(&mut self) -> Cow<'a, str> {
        let mut output = String::new();
        for query in self.arguments.to_owned() {
            let (key, value) = query;
            if output.len() >= 1 {
                output.push_str(" ");
            }
            let format_string = format!("--{} {}", &*key, Escape(&*value));
            output.push_str(format_string.as_str());
        }
        Cow::Owned(output)
    }

    ///
    /// Create Groonga command line style command.
    pub fn encode(&mut self) -> String {
        format!("{} {}",
                self.get_command().into_owned(),
                self.make_command().into_owned())
            .trim()
            .to_string()
    }
}

#[cfg(test)]
mod test {
    use super::CommandLine;
    use select::SelectCommand;
    use load::{LoadCommand, InputType};
    use status::StatusCommand;
    use dump::DumpCommand;
    use delete::DeleteCommand;
    use table_create::TableCreateCommand;
    use types::table_flag_type::TableFlagType;
    use column_create::ColumnCreateCommand;
    use types::data_type::DataType;

    #[test]
    fn test_select() {
        let (command, query) = SelectCommand::new("Test".to_string())
            .filter("output_column @ \"type_safe\"".to_string())
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "select --table Test --filter \'output_column @ \"type_safe\"\'";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_load() {
        let load_data: &'static str = r#"[
{"_key":"http://example.org/","title":"This is test record 1!"},
]"#;
        let (command, query, values) = LoadCommand::new("Test".to_string(), load_data.to_string())
            .input_type(InputType::Json)
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "load --table Test --input_type json";
        assert_eq!(cli_encoded.to_string(), command_line);
        assert_eq!(load_data.to_string(), values);
    }

    #[test]
    fn test_status() {
        let (command, query) = StatusCommand::new().build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "status";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_dump() {
        let (command, query) = DumpCommand::new()
            .tables(vec!["Books".to_string(), "Categories".to_string()])
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "dump --tables Books,Categories";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_delete() {
        let (command, query) = DeleteCommand::new("Books".to_string())
            .filter("author == unknown".to_string())
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "delete --table Books --filter \'author == unknown\'";
        assert_eq!(cli_encoded.to_string(), command_line);
    }

    #[test]
    fn test_table_create() {
        let (command, query) = TableCreateCommand::new("Test".to_string())
            .flags(vec![(TableFlagType::PatKey), (TableFlagType::KeyWithSIS)])
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let url_encoded = "table_create --name Test --flags TABLE_PAT_KEY|KEY_WITH_SIS";
        assert_eq!(url_encoded.to_string(), command_line);
    }

    #[test]
    fn test_column_create() {
        let (command, query) = ColumnCreateCommand::new("Test".to_string(), "element".to_string())
            .column_type(DataType::LongText)
            .build();
        let command_line = CommandLine::new(command, query).encode();
        let cli_encoded = "column_create --table Test --name element --type LongText";
        assert_eq!(cli_encoded.to_string(), command_line);
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io::ErrorKind;
use std::process::Command;

mod documents;
mod exercises;
mod feeds;
mod git;
mod homebrew;
mod notes;
mod paths;
mod processes;
mod website;

pub fn setup() {
    // Vim
    homebrew::install_package("vim", &|| {
        let vim_plugins_path = paths::vim_plugins();
        let vim_configuration_path = paths::vim_configuration();

        paths::create_dir(&vim_plugins_path, &|| {
            git::clone(&vim_plugins_path, "https://github.com/tpope/vim-sensible");
            git::clone(&vim_plugins_path, "https://github.com/tpope/vim-sleuth");
            git::clone(&vim_plugins_path, "https://github.com/sheerun/vim-polyglot");
            git::clone(&vim_plugins_path, "https://github.com/octref/RootIgnore");
            git::clone(&vim_plugins_path, "https://github.com/dense-analysis/ale");
        });

        paths::create_file(&vim_configuration_path, &|| match fs::write(
            &vim_configuration_path,
            "set grepprg=rg\\ --vimgrep\nset grepformat=%f:%l:%c:%m",
        ) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        });
    });
    homebrew::install_package("ripgrep", &|| {});

    // Git
    homebrew::install_package("git", &|| {});

    // Rust
    homebrew::install_package("rustup-init", &|| {
        let rustup_path_check = match Command::new("which").arg("rustup").output() {
            Ok(output) => output.stdout,
            Err(error) => panic!("There was a problem: {:?}", error),
        };

        if rustup_path_check.is_empty() {
            match Command::new("rustup-init").output() {
                Ok(_) => (),
                Err(error) => match error.kind() {
                    ErrorKind::NotFound => panic!("The rustup-init command is missing."),
                    other_error => panic!("There was a problem: {:?}", other_error),
                },
            }
        }
    });

    // JavaScript
    homebrew::install_package("node", &|| {});
}

pub fn upgrade() {
    // Unix
    homebrew::upgrade_self();

    // Vim
    homebrew::upgrade_package("vim");
    homebrew::upgrade_package("ripgrep");
    git::pull_all(&paths::vim_plugins());

    // Git
    homebrew::upgrade_package("git");

    // Rust
    homebrew::upgrade_package("rustup-init");
    homebrew::upgrade_rust();

    // JavaScript
    homebrew::upgrade_package("node");
}

pub fn end() {
    // Unix
    homebrew::clean_artifacts();

    // Vim
    processes::stop("vim");

    // Git
    git::check_all(&paths::repos());

    // Rust
    processes::stop("rls");

    // JavaScript
    processes::stop("node");
}

pub fn study() {
    feeds::next();
    exercises::next();
    documents::next();
    notes::next();
    website::release();
}
<commit_msg>Simplify multiline string<commit_after>use std::fs;
use std::io::ErrorKind;
use std::process::Command;

mod documents;
mod exercises;
mod feeds;
mod git;
mod homebrew;
mod notes;
mod paths;
mod processes;
mod website;

pub fn setup() {
    // Vim
    homebrew::install_package("vim", &|| {
        let vim_plugins_path = paths::vim_plugins();
        let vim_configuration_path = paths::vim_configuration();

        paths::create_dir(&vim_plugins_path, &|| {
            git::clone(&vim_plugins_path, "https://github.com/tpope/vim-sensible");
            git::clone(&vim_plugins_path, "https://github.com/tpope/vim-sleuth");
            git::clone(&vim_plugins_path, "https://github.com/sheerun/vim-polyglot");
            git::clone(&vim_plugins_path, "https://github.com/octref/RootIgnore");
            git::clone(&vim_plugins_path, "https://github.com/dense-analysis/ale");
        });

        paths::create_file(&vim_configuration_path, &|| match fs::write(
            &vim_configuration_path,
            "set grepprg=rg\\ --vimgrep
            set grepformat=%f:%l:%c:%m",
        ) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        });
    });
    homebrew::install_package("ripgrep", &|| {});

    // Git
    homebrew::install_package("git", &|| {});

    // Rust
    homebrew::install_package("rustup-init", &|| {
        let rustup_path_check = match Command::new("which").arg("rustup").output() {
            Ok(output) => output.stdout,
            Err(error) => panic!("There was a problem: {:?}", error),
        };

        if rustup_path_check.is_empty() {
            match Command::new("rustup-init").output() {
                Ok(_) => (),
                Err(error) => match error.kind() {
                    ErrorKind::NotFound => panic!("The rustup-init command is missing."),
                    other_error => panic!("There was a problem: {:?}", other_error),
                },
            }
        }
    });

    // JavaScript
    homebrew::install_package("node", &|| {});
}

pub fn upgrade() {
    // Unix
    homebrew::upgrade_self();

    // Vim
    homebrew::upgrade_package("vim");
    homebrew::upgrade_package("ripgrep");
    git::pull_all(&paths::vim_plugins());

    // Git
    homebrew::upgrade_package("git");

    // Rust
    homebrew::upgrade_package("rustup-init");
    homebrew::upgrade_rust();

    // JavaScript
    homebrew::upgrade_package("node");
}

pub fn end() {
    // Unix
    homebrew::clean_artifacts();

    // Vim
    processes::stop("vim");

    // Git
    git::check_all(&paths::repos());

    // Rust
    processes::stop("rls");

    // JavaScript
    processes::stop("node");
}

pub fn study() {
    feeds::next();
    exercises::next();
    documents::next();
    notes::next();
    website::release();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019 Jeehoon Kang
 *
 * 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
 *
 *     https://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 core::mem::{self, MaybeUninit};
use core::ptr;

use crate::addr::*;
use crate::arch::*;
use crate::mm::*;
use crate::page::*;
use crate::spinlock::*;
use crate::std::*;
use crate::types::*;
use crate::vm::*;

/// From inc/hf/arch/cpu.h.
extern "C" {
    /// Disables interrupts.
    fn arch_irq_enable();

    /// Enables interrupts.
    fn arch_irq_disable();

    /// Reset the register values other than the PC and argument which are set with
    /// `arch_regs_set_pc_arg()`.
    fn arch_regs_reset(
        r: *mut ArchRegs,
        is_primary: bool,
        vm_id: spci_vm_id_t,
        vcpu_id: cpu_id_t,
        table: paddr_t,
    );

    /// Updates the given registers so that when a vcpu runs, it starts off at the
    /// given address (pc) with the given argument.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_pc_arg(r: *mut ArchRegs, pc: ipaddr_t, arg: uintreg_t);

    /// Updates the register holding the return value of a function.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_retval(r: *mut ArchRegs, v: uintreg_t);
}

const STACK_SIZE: usize = PAGE_SIZE;

/// The number of bits in each element of the interrupt bitfields.
const INTERRUPT_REGISTER_BITS: usize = 32;

#[repr(C)]
pub enum VCpuStatus {
    /// The vcpu is switched off.
    Off,

    /// The vcpu is ready to be run.
    Ready,

    /// The vcpu is currently running.
    Running,

    /// The vcpu is waiting for a message.
    BlockedMailbox,

    /// The vcpu is waiting for an interrupt.
    BlockedInterrupt,

    /// The vcpu has aborted.
    Aborted,
}

#[repr(C)]
pub struct Interrupts {
    /// Bitfield keeping track of which interrupts are enabled.
    enabled: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// Bitfield keeping track of which interrupts are pending.
    pending: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// The number of interrupts which are currently both enabled and pending. i.e. the number of
    /// bits set in enabled & pending.
    enabled_and_pending_count: u32,
}

#[repr(C)]
pub struct VCpuFaultInfo {
    ipaddr: ipaddr_t,
    vaddr: vaddr_t,
    pc: vaddr_t,
    mode: c_int,
}

#[repr(C)]
pub struct VCpu {
    lock: RawSpinLock,

    /// The state is only changed in the context of the vCPU being run. This ensures the scheduler
    /// can easily keep track of the vCPU state as transitions are indicated by the return code from
    /// the run call.
    state: VCpuStatus,

    cpu: *mut Cpu,
    vm: *mut Vm,
    regs: ArchRegs,
    interrupts: Interrupts,

    /// Determine whether the 'regs' field is available for use. This is set
    /// to false when a vCPU is about to run on a physical CPU, and is set
    /// back to true when it is descheduled.
    regs_available: bool,
}

/// Encapsulates a vCPU whose lock is held.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VCpuLocked {
    vcpu: *mut VCpu,
}

// TODO: Update alignment such that cpus are in different cache lines.
#[repr(C)]
pub struct Cpu {
    /// CPU identifier. Doesn't have to be contiguous.
    id: cpu_id_t,

    /// Pointer to bottom of the stack.
    stack_bottom: *mut c_void,

    /// Enabling/disabling irqs are counted per-cpu. They are enabled when the count is zero, and
    /// disabled when it's non-zero.
    irq_disable_count: u32,

    /// See api.c for the partial ordering on locks.
    lock: RawSpinLock,

    /// Determines whether or not the cpu is currently on.
    is_on: bool,
}

unsafe impl Sync for Cpu {}

// TODO: alignas(2 * sizeof(uintreg_t))
// #[repr(align(16))]
#[no_mangle]
static mut callstacks: MaybeUninit<[[u8; STACK_SIZE]; MAX_CPUS]> = MaybeUninit::uninit();

/// State of all supported CPUs. The stack of the first one is initialized.
/// struct cpu cpus[MAX_CPUS] = {
///     {
// 	        .is_on = 1,
///         .stack_bottom = &callstacks[0][STACK_SIZE],
///     },
/// };
/// static mut CPUS: MaybeUninit<[Cpu; MAX_CPUS]> = MaybeUninit::uninit();
static mut cpu_count: u32 = 1;

/// Kernel loader writes booted CPU ID on `cpus.id` and initializes the CPU
/// stack by the address in `cpus.stack_bottom`
/// (See src/arch/aarch64/hypervisor/plat_entry.S and cpu_entry.S.)
/// TODO: Any better design?
/*
#[no_mangle]
pub static mut cpus: Cpu = Cpu {
    id: 0,
    stack_bottom: unsafe { (&callstacks as *const _ as usize) as _ },
    irq_disable_count: 0,
    lock: RawSpinLock::new(),
    is_on: true,
};*/
extern "C" {
    #[no_mangle]
    static mut cpus: MaybeUninit<[Cpu; MAX_CPUS]>;
}

#[no_mangle]
pub unsafe extern "C" fn cpu_init(c: *mut Cpu) {
    // TODO: Assumes that c is zeroed out already.
    sl_init(&mut (*c).lock);
}

#[no_mangle]
pub unsafe extern "C" fn cpu_module_init(cpu_ids: *mut cpu_id_t, count: usize) {
    let mut j: u32;
    let boot_cpu_id: cpu_id_t = cpus.get_ref()[0].id;
    let mut found_boot_cpu: bool = false;

    cpu_count = count as u32;

    // Initialize CPUs with the IDs from the configuration passed in. The
    // CPUs after the boot CPU are initialized in reverse order. The boot
    // CPU is initialized when it is found or in place of the last CPU if it
    // is not found.
    j = cpu_count;
    for i in 0..cpu_count {
        let c: *mut Cpu;
        let id: cpu_id_t = *cpu_ids.offset(i as isize);

        if found_boot_cpu || id != boot_cpu_id {
            j -= 1;
            c = &mut cpus.get_mut()[j as usize];
        } else {
            found_boot_cpu = true;
            c = &mut cpus.get_mut()[0];
        }

        cpu_init(c);
        (*c).id = id;
        {
            let callstacks_i = callstacks.get_mut()[i as usize].as_mut_ptr();
            (*c).stack_bottom = &mut *callstacks_i.offset(STACK_SIZE as isize) as *mut _ as _;

            // Note: referencing callstacks.get_mut()[i as usize][STACK_SIZE] directly
            // causes 'index out of bounds' error on compile time.
        }
    }

    if !found_boot_cpu {
        // Boot CPU was initialized but with wrong ID.
        dlog!("Boot CPU's ID not found in config.");
        cpus.get_mut()[0].id = boot_cpu_id;
    }
}

#[no_mangle]
pub unsafe extern "C" fn cpu_index(c: *mut Cpu) -> usize {
    c.offset_from(cpus.get_ref().as_ptr()) as usize
}

/// Turns CPU on and returns the previous state.
#[no_mangle]
pub unsafe extern "C" fn cpu_on(c: *mut Cpu, entry: ipaddr_t, arg: uintreg_t) -> bool {
    let prev: bool;

    sl_lock(&(*c).lock);
    prev = (*c).is_on;
    (*c).is_on = true;
    sl_unlock(&(*c).lock);

    if !prev {
        let vm = vm_find(HF_PRIMARY_VM_ID);
        let vcpu = vm_get_vcpu(vm, cpu_index(c) as u16);
        let mut vcpu_locked = vcpu_lock(vcpu);

        vcpu_on(vcpu_locked, entry, arg);
        vcpu_unlock(&mut vcpu_locked);
    }

    prev
}

/// Prepares the CPU for turning itself off.
#[no_mangle]
pub unsafe extern "C" fn cpu_off(c: *mut Cpu) {
    sl_lock(&(*c).lock);
    (*c).is_on = true;
    sl_unlock(&(*c).lock);
}

/// Searches for a CPU based on its id.
#[no_mangle]
pub unsafe extern "C" fn cpu_find(id: cpu_id_t) -> *mut Cpu {
    for i in 0usize..cpu_count as usize {
        if cpus.get_ref()[i].id == id {
            return &mut cpus.get_mut()[i];
        }
    }

    ptr::null_mut()
}

/// Locks the given vCPU and updates `locked` to hold the newly locked vCPU.
#[no_mangle]
pub unsafe extern "C" fn vcpu_lock(vcpu: *mut VCpu) -> VCpuLocked {
    sl_lock(&(*vcpu).lock);

    VCpuLocked { vcpu }
}

/// Unlocks a vCPU previously locked with vcpu_lock, and updates `locked` to
/// reflect the fact that the vCPU is no longer locked.
#[no_mangle]
pub unsafe extern "C" fn vcpu_unlock(locked: *mut VCpuLocked) {
    sl_unlock(&(*(*locked).vcpu).lock);
    (*locked).vcpu = ptr::null_mut();
}

#[no_mangle]
pub unsafe extern "C" fn vcpu_init(vcpu: *mut VCpu, vm: *mut Vm) {
    memset_s(vcpu as _, mem::size_of::<VCpu>(), 0, mem::size_of::<VCpu>());
    sl_init(&mut (*vcpu).lock);
    (*vcpu).regs_available = true;
    (*vcpu).vm = vm;
    (*vcpu).state = VCpuStatus::Off;
}

/// Initialise the registers for the given vCPU and set the state to
/// VCPU_STATE_READY. The caller must hold the vCPU lock while calling this.
#[no_mangle]
pub unsafe extern "C" fn vcpu_on(vcpu: VCpuLocked, entry: ipaddr_t, arg: uintreg_t) {
    arch_regs_set_pc_arg(&mut (*vcpu.vcpu).regs, entry, arg);
    (*vcpu.vcpu).state = VCpuStatus::Ready;
}

#[no_mangle]
pub unsafe extern "C" fn vcpu_index(vcpu: *const VCpu) -> spci_vcpu_index_t {
    let vcpus = (*(*vcpu).vm).vcpus.as_ptr();
    let index = vcpu.offset_from(vcpus);
    assert!(index < core::u16::MAX as isize);
    index as u16
}

/// Check whether the given vcpu_state is an off state, for the purpose of
/// turning vCPUs on and off. Note that aborted still counts as on in this
/// context.
#[no_mangle]
pub unsafe extern "C" fn vcpu_is_off(vcpu: VCpuLocked) -> bool {
    match (*vcpu.vcpu).state {
        VCpuStatus::Off => true,
        _ =>
        // Aborted still counts as ON for the purposes of PSCI,
        // because according to the PSCI specification (section
        // 5.7.1) a core is only considered to be off if it has
        // been turned off with a CPU_OFF call or hasn't yet
        // been turned on with a CPU_ON call.
        {
            false
        }
    }
}

/// Starts a vCPU of a secondary VM.
///
/// Returns true if the secondary was reset and started, or false if it was
/// already on and so nothing was done.
#[no_mangle]
pub unsafe extern "C" fn vcpu_secondary_reset_and_start(
    vcpu: *mut VCpu,
    entry: ipaddr_t,
    arg: uintreg_t,
) -> bool {
    let mut vcpu_locked;
    let vm = (*vcpu).vm;
    let vcpu_was_off;

    assert!((*vm).id != HF_PRIMARY_VM_ID);

    vcpu_locked = vcpu_lock(vcpu);
    vcpu_was_off = vcpu_is_off(vcpu_locked);
    if vcpu_was_off {
        // Set vCPU registers to a clean state ready for boot. As this
        // is a secondary which can migrate between pCPUs, the ID of the
        // vCPU is defined as the index and does not match the ID of the
        // pCPU it is running on.
        arch_regs_reset(
            &mut (*vcpu).regs,
            false,
            (*vm).id,
            vcpu_index(vcpu) as cpu_id_t,
            pa_init((*vm).ptable.root),
        );
        vcpu_on(vcpu_locked, entry, arg);
    }

    vcpu_unlock(&mut vcpu_locked);
    vcpu_was_off
}

/// Handles a page fault. It does so by determining if it's a legitimate or
/// spurious fault, and recovering from the latter.
///
/// Returns true if the caller should resume the current vcpu, or false if its
/// VM should be aborted.
#[no_mangle]
pub unsafe extern "C" fn vcpu_handle_page_fault(
    current: *const VCpu,
    f: *mut VCpuFaultInfo,
) -> bool {
    let vm = (*current).vm;
    let mut mode = mem::uninitialized(); // to avoid use-of-uninitialized error
    let mask = (*f).mode | Mode::INVALID.bits() as i32;
    let resume;

    sl_lock(&(*vm).lock);

    // Check if this is a legitimate fault, i.e., if the page table doesn't
    // allow the access attemped by the VM.
    //
    // Otherwise, this is a spurious fault, likely because another CPU is
    // updating the page table. It is responsible for issuing global TLB
    // invalidations while holding the VM lock, so we don't need to do
    // anything else to recover from it. (Acquiring/releasing the lock
    // ensured that the invalidations have completed.)
    resume = mm_vm_get_mode(
        &mut (*vm).ptable,
        (*f).ipaddr,
        ipa_add((*f).ipaddr, 1),
        &mut mode,
    ) && (mode & mask) == (*f).mode;

    sl_unlock(&(*vm).lock);

    if !resume {
        dlog!(
            "Stage-2 page fault: pc=0x{}, vmid={}, vcpu={}, vaddr=0x{}, ipaddr=0x{}, mode=0x{}\n",
            (*f).pc,
            (*vm).id,
            vcpu_index(current),
            (*f).vaddr,
            (*f).ipaddr,
            (*f).mode
        );
    }

    resume
}
<commit_msg>Add some comments, and remove `Sync` marker on `Cpu`.<commit_after>/*
 * Copyright 2019 Jeehoon Kang
 *
 * 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
 *
 *     https://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 core::mem::{self, MaybeUninit};
use core::ptr;

use crate::addr::*;
use crate::arch::*;
use crate::mm::*;
use crate::page::*;
use crate::spinlock::*;
use crate::std::*;
use crate::types::*;
use crate::vm::*;

/// From inc/hf/arch/cpu.h.
extern "C" {
    /// Disables interrupts.
    fn arch_irq_enable();

    /// Enables interrupts.
    fn arch_irq_disable();

    /// Reset the register values other than the PC and argument which are set with
    /// `arch_regs_set_pc_arg()`.
    fn arch_regs_reset(
        r: *mut ArchRegs,
        is_primary: bool,
        vm_id: spci_vm_id_t,
        vcpu_id: cpu_id_t,
        table: paddr_t,
    );

    /// Updates the given registers so that when a vcpu runs, it starts off at the
    /// given address (pc) with the given argument.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_pc_arg(r: *mut ArchRegs, pc: ipaddr_t, arg: uintreg_t);

    /// Updates the register holding the return value of a function.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_retval(r: *mut ArchRegs, v: uintreg_t);
}

const STACK_SIZE: usize = PAGE_SIZE;

/// The number of bits in each element of the interrupt bitfields.
const INTERRUPT_REGISTER_BITS: usize = 32;

#[repr(C)]
pub enum VCpuStatus {
    /// The vcpu is switched off.
    Off,

    /// The vcpu is ready to be run.
    Ready,

    /// The vcpu is currently running.
    Running,

    /// The vcpu is waiting for a message.
    BlockedMailbox,

    /// The vcpu is waiting for an interrupt.
    BlockedInterrupt,

    /// The vcpu has aborted.
    Aborted,
}

#[repr(C)]
pub struct Interrupts {
    /// Bitfield keeping track of which interrupts are enabled.
    enabled: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// Bitfield keeping track of which interrupts are pending.
    pending: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// The number of interrupts which are currently both enabled and pending. i.e. the number of
    /// bits set in enabled & pending.
    enabled_and_pending_count: u32,
}

#[repr(C)]
pub struct VCpuFaultInfo {
    ipaddr: ipaddr_t,
    vaddr: vaddr_t,
    pc: vaddr_t,
    mode: c_int,
}

#[repr(C)]
pub struct VCpu {
    lock: RawSpinLock,

    /// The state is only changed in the context of the vCPU being run. This ensures the scheduler
    /// can easily keep track of the vCPU state as transitions are indicated by the return code from
    /// the run call.
    state: VCpuStatus,

    cpu: *mut Cpu,
    vm: *mut Vm,
    regs: ArchRegs,
    interrupts: Interrupts,

    /// Determine whether the 'regs' field is available for use. This is set
    /// to false when a vCPU is about to run on a physical CPU, and is set
    /// back to true when it is descheduled.
    regs_available: bool,
}

/// Encapsulates a vCPU whose lock is held.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VCpuLocked {
    vcpu: *mut VCpu,
}

// TODO: Update alignment such that cpus are in different cache lines.
#[repr(C)]
pub struct Cpu {
    /// CPU identifier. Doesn't have to be contiguous.
    id: cpu_id_t,

    /// Pointer to bottom of the stack.
    stack_bottom: *mut c_void,

    /// Enabling/disabling irqs are counted per-cpu. They are enabled when the count is zero, and
    /// disabled when it's non-zero.
    irq_disable_count: u32,

    /// See api.c for the partial ordering on locks.
    lock: RawSpinLock,

    /// Determines whether or not the cpu is currently on.
    is_on: bool,
}

// unsafe impl Sync for Cpu {}

/// The stack to be used by the CPUs.
/// TODO: alignas(2 * sizeof(uintreg_t))
#[no_mangle]
static mut callstacks: MaybeUninit<[[u8; STACK_SIZE]; MAX_CPUS]> = MaybeUninit::uninit();

/// State of all supported CPUs. The stack of the first one is initialized.
/// Kernel loader writes booted CPU ID on `cpus.id` and initializes the CPU
/// stack by the address in `cpus.stack_bottom`.
/// (See src/arch/aarch64/hypervisor/plat_entry.S and cpu_entry.S.)
///
/// Initializing static variables with pointers in Rust failed here. We left
/// the initialization code of `cpus` in `src/cpu.c`.
extern "C" {
    #[no_mangle]
    static mut cpus: MaybeUninit<[Cpu; MAX_CPUS]>;
}

static mut cpu_count: u32 = 1;

#[no_mangle]
pub unsafe extern "C" fn cpu_init(c: *mut Cpu) {
    // TODO: Assumes that c is zeroed out already.
    sl_init(&mut (*c).lock);
}

#[no_mangle]
pub unsafe extern "C" fn cpu_module_init(cpu_ids: *mut cpu_id_t, count: usize) {
    let mut j: u32;
    let boot_cpu_id: cpu_id_t = cpus.get_ref()[0].id;
    let mut found_boot_cpu: bool = false;

    cpu_count = count as u32;

    // Initialize CPUs with the IDs from the configuration passed in. The
    // CPUs after the boot CPU are initialized in reverse order. The boot
    // CPU is initialized when it is found or in place of the last CPU if it
    // is not found.
    j = cpu_count;
    for i in 0..cpu_count {
        let c: *mut Cpu;
        let id: cpu_id_t = *cpu_ids.offset(i as isize);

        if found_boot_cpu || id != boot_cpu_id {
            j -= 1;
            c = &mut cpus.get_mut()[j as usize];
        } else {
            found_boot_cpu = true;
            c = &mut cpus.get_mut()[0];
        }

        cpu_init(c);
        (*c).id = id;
        {
            let callstacks_i = callstacks.get_mut()[i as usize].as_mut_ptr();
            (*c).stack_bottom = &mut *callstacks_i.offset(STACK_SIZE as isize) as *mut _ as _;

            // Note: referencing callstacks.get_mut()[i as usize][STACK_SIZE] directly
            // causes 'index out of bounds' error on compile time.
        }
    }

    if !found_boot_cpu {
        // Boot CPU was initialized but with wrong ID.
        dlog!("Boot CPU's ID not found in config.");
        cpus.get_mut()[0].id = boot_cpu_id;
    }
}

#[no_mangle]
pub unsafe extern "C" fn cpu_index(c: *mut Cpu) -> usize {
    c.offset_from(cpus.get_ref().as_ptr()) as usize
}

/// Turns CPU on and returns the previous state.
#[no_mangle]
pub unsafe extern "C" fn cpu_on(c: *mut Cpu, entry: ipaddr_t, arg: uintreg_t) -> bool {
    let prev: bool;

    sl_lock(&(*c).lock);
    prev = (*c).is_on;
    (*c).is_on = true;
    sl_unlock(&(*c).lock);

    if !prev {
        let vm = vm_find(HF_PRIMARY_VM_ID);
        let vcpu = vm_get_vcpu(vm, cpu_index(c) as u16);
        let mut vcpu_locked = vcpu_lock(vcpu);

        vcpu_on(vcpu_locked, entry, arg);
        vcpu_unlock(&mut vcpu_locked);
    }

    prev
}

/// Prepares the CPU for turning itself off.
#[no_mangle]
pub unsafe extern "C" fn cpu_off(c: *mut Cpu) {
    sl_lock(&(*c).lock);
    (*c).is_on = true;
    sl_unlock(&(*c).lock);
}

/// Searches for a CPU based on its id.
#[no_mangle]
pub unsafe extern "C" fn cpu_find(id: cpu_id_t) -> *mut Cpu {
    for i in 0usize..cpu_count as usize {
        if cpus.get_ref()[i].id == id {
            return &mut cpus.get_mut()[i];
        }
    }

    ptr::null_mut()
}

/// Locks the given vCPU and updates `locked` to hold the newly locked vCPU.
#[no_mangle]
pub unsafe extern "C" fn vcpu_lock(vcpu: *mut VCpu) -> VCpuLocked {
    sl_lock(&(*vcpu).lock);

    VCpuLocked { vcpu }
}

/// Unlocks a vCPU previously locked with vcpu_lock, and updates `locked` to
/// reflect the fact that the vCPU is no longer locked.
#[no_mangle]
pub unsafe extern "C" fn vcpu_unlock(locked: *mut VCpuLocked) {
    sl_unlock(&(*(*locked).vcpu).lock);
    (*locked).vcpu = ptr::null_mut();
}

#[no_mangle]
pub unsafe extern "C" fn vcpu_init(vcpu: *mut VCpu, vm: *mut Vm) {
    memset_s(vcpu as _, mem::size_of::<VCpu>(), 0, mem::size_of::<VCpu>());
    sl_init(&mut (*vcpu).lock);
    (*vcpu).regs_available = true;
    (*vcpu).vm = vm;
    (*vcpu).state = VCpuStatus::Off;
}

/// Initialise the registers for the given vCPU and set the state to
/// VCPU_STATE_READY. The caller must hold the vCPU lock while calling this.
#[no_mangle]
pub unsafe extern "C" fn vcpu_on(vcpu: VCpuLocked, entry: ipaddr_t, arg: uintreg_t) {
    arch_regs_set_pc_arg(&mut (*vcpu.vcpu).regs, entry, arg);
    (*vcpu.vcpu).state = VCpuStatus::Ready;
}

#[no_mangle]
pub unsafe extern "C" fn vcpu_index(vcpu: *const VCpu) -> spci_vcpu_index_t {
    let vcpus = (*(*vcpu).vm).vcpus.as_ptr();
    let index = vcpu.offset_from(vcpus);
    assert!(index < core::u16::MAX as isize);
    index as u16
}

/// Check whether the given vcpu_state is an off state, for the purpose of
/// turning vCPUs on and off. Note that aborted still counts as on in this
/// context.
#[no_mangle]
pub unsafe extern "C" fn vcpu_is_off(vcpu: VCpuLocked) -> bool {
    match (*vcpu.vcpu).state {
        VCpuStatus::Off => true,
        _ =>
        // Aborted still counts as ON for the purposes of PSCI,
        // because according to the PSCI specification (section
        // 5.7.1) a core is only considered to be off if it has
        // been turned off with a CPU_OFF call or hasn't yet
        // been turned on with a CPU_ON call.
        {
            false
        }
    }
}

/// Starts a vCPU of a secondary VM.
///
/// Returns true if the secondary was reset and started, or false if it was
/// already on and so nothing was done.
#[no_mangle]
pub unsafe extern "C" fn vcpu_secondary_reset_and_start(
    vcpu: *mut VCpu,
    entry: ipaddr_t,
    arg: uintreg_t,
) -> bool {
    let mut vcpu_locked;
    let vm = (*vcpu).vm;
    let vcpu_was_off;

    assert!((*vm).id != HF_PRIMARY_VM_ID);

    vcpu_locked = vcpu_lock(vcpu);
    vcpu_was_off = vcpu_is_off(vcpu_locked);
    if vcpu_was_off {
        // Set vCPU registers to a clean state ready for boot. As this
        // is a secondary which can migrate between pCPUs, the ID of the
        // vCPU is defined as the index and does not match the ID of the
        // pCPU it is running on.
        arch_regs_reset(
            &mut (*vcpu).regs,
            false,
            (*vm).id,
            vcpu_index(vcpu) as cpu_id_t,
            pa_init((*vm).ptable.root),
        );
        vcpu_on(vcpu_locked, entry, arg);
    }

    vcpu_unlock(&mut vcpu_locked);
    vcpu_was_off
}

/// Handles a page fault. It does so by determining if it's a legitimate or
/// spurious fault, and recovering from the latter.
///
/// Returns true if the caller should resume the current vcpu, or false if its
/// VM should be aborted.
#[no_mangle]
pub unsafe extern "C" fn vcpu_handle_page_fault(
    current: *const VCpu,
    f: *mut VCpuFaultInfo,
) -> bool {
    let vm = (*current).vm;
    let mut mode = mem::uninitialized(); // to avoid use-of-uninitialized error
    let mask = (*f).mode | Mode::INVALID.bits() as i32;
    let resume;

    sl_lock(&(*vm).lock);

    // Check if this is a legitimate fault, i.e., if the page table doesn't
    // allow the access attemped by the VM.
    //
    // Otherwise, this is a spurious fault, likely because another CPU is
    // updating the page table. It is responsible for issuing global TLB
    // invalidations while holding the VM lock, so we don't need to do
    // anything else to recover from it. (Acquiring/releasing the lock
    // ensured that the invalidations have completed.)
    resume = mm_vm_get_mode(
        &mut (*vm).ptable,
        (*f).ipaddr,
        ipa_add((*f).ipaddr, 1),
        &mut mode,
    ) && (mode & mask) == (*f).mode;

    sl_unlock(&(*vm).lock);

    if !resume {
        dlog!(
            "Stage-2 page fault: pc=0x{}, vmid={}, vcpu={}, vaddr=0x{}, ipaddr=0x{}, mode=0x{}\n",
            (*f).pc,
            (*vm).id,
            vcpu_index(current),
            (*f).vaddr,
            (*f).ipaddr,
            (*f).mode
        );
    }

    resume
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate helix;

ruby! {
    reopen class Array {
        def is_superset_of(&self, needle: &[usize]) -> bool {
            if needle.is_empty() { return true }

            let haystack = self.as_ref();

            if haystack.is_empty() { return false }

            let mut needle = needle.iter();
            let mut needle_item = needle.next().unwrap();

            for item in haystack {
                if item == needle_item {
                    match needle.next() {
                        None => return true,
                        Some(next_item) => needle_item = next_item
                    }
                }
            }

            false
        }
    }
}

// Delete me:

use helix::{FromRuby, ToRust};

impl AsRef<[usize]> for Array {
    fn as_ref(&self) -> &[usize] {
        let checked = FromRuby::from_ruby(self.helix);
        checked.unwrap().to_rust()
    }
}
<commit_msg>Fix membership test<commit_after>#[macro_use]
extern crate helix;

pub struct RubyArray<'a>(&'a [usize]);

ruby! {
    reopen class Array {
        def is_superset_of(&self, needle: RubyArray) -> bool {
            let needle = needle.0;

            if needle.is_empty() { return true }

            let haystack = self.as_ref();

            if haystack.is_empty() { return false }

            let mut needle = needle.iter();
            let mut needle_item = needle.next().unwrap();

            for item in haystack {
                if item == needle_item {
                    match needle.next() {
                        None => return true,
                        Some(next_item) => needle_item = next_item
                    }
                }
            }

            false
        }
    }
}

use helix::{FromRuby, CheckedValue, CheckResult, ToRust, sys};

impl<'a> FromRuby for RubyArray<'a> {
    fn from_ruby(value: sys::VALUE) -> CheckResult<RubyArray<'a>> {
        if unsafe { sys::RB_TYPE_P(value, sys::T_ARRAY) } {
            Ok(unsafe { CheckedValue::new(value) })
        } else {
            type_error!(value, "an Array of unsigned pointer-sized integers")
        }
    }
}

impl<'a> ToRust<RubyArray<'a>> for CheckedValue<RubyArray<'a>> {
    fn to_rust(self) -> RubyArray<'a> {
        let size = unsafe { sys::RARRAY_LEN(self.inner) };
        let ptr = unsafe { sys::RARRAY_PTR(self.inner) };
        RubyArray(unsafe { std::slice::from_raw_parts(ptr as *const usize, size as usize) })
    }
}

impl AsRef<[usize]> for Array {
    fn as_ref<'a>(&'a self) -> &'a [usize] {
        let checked = RubyArray::from_ruby(self.helix);
        checked.unwrap().to_rust().0
    }
}
<|endoftext|>