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

//! Implementation of the `build` subcommand, used to compile a book.

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::path::{Path, PathBuf};
use rustc_back::tempdir::TempDir;

use subcommand::Subcommand;
use term::Term;
use error::{err, CliResult, CommandResult};
use book;
use book::{Book, BookItem};
use css;
use javascript;

use rustdoc;

struct Build;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "build" {
        Some(Box::new(Build))
    } else {
        None
    }
}

fn write_toc(book: &Book, path_to_root: &Path, out: &mut Write) -> io::Result<()> {
    fn walk_items(items: &[BookItem],
                  section: &str,
                  path_to_root: &Path,
                  out: &mut Write) -> io::Result<()> {
        for (i, item) in items.iter().enumerate() {
            try!(walk_item(item, &format!("{}{}.", section, i + 1)[..], path_to_root, out));
        }
        Ok(())
    }
    fn walk_item(item: &BookItem,
                 section: &str,
                 path_to_root: &Path,
                 out: &mut Write) -> io::Result<()> {
        try!(writeln!(out, "
{} {}",
                 path_to_root.join(&item.path.with_extension("html")).display(),
                 section,
                 item.title));
        if !item.children.is_empty() {
            try!(writeln!(out, "
"));
            let _ = walk_items(&item.children[..], section, path_to_root, out);
            try!(writeln!(out, "
"));
        }
        try!(writeln!(out, "
"));

        Ok(())
    }

    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));
    try!(walk_items(&book.chapters[..], "", path_to_root, out));
    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));

    Ok(())
}

fn render(book: &Book, tgt: &Path) -> CliResult<()> {
    let tmp = try!(TempDir::new("rust-book"));

    for (_section, item) in book.iter() {
        let out_path = match item.path.parent() {
            Some(p) => tgt.join(p),
            None => tgt.to_path_buf(),
        };

        let src;
        if env::args().len() < 3 {
            src = env::current_dir().unwrap().clone();
        } else {
            src = PathBuf::from(&env::args().nth(2).unwrap());
        }
        // preprocess the markdown, rerouting markdown references to html
        // references
        let mut markdown_data = String::new();
        try!(File::open(&src.join(&item.path)).and_then(|mut f| {
            f.read_to_string(&mut markdown_data)
        }));
        let preprocessed_path = tmp.path().join(item.path.file_name().unwrap());
        {
            let urls = markdown_data.replace(".md)", ".html)");
            try!(File::create(&preprocessed_path).and_then(|mut f| {
                f.write_all(urls.as_bytes())
            }));
        }

        // write the prelude to a temporary HTML file for rustdoc inclusion
        let prelude = tmp.path().join("prelude.html");
        {
            let mut toc = BufWriter::new(try!(File::create(&prelude)));
            try!(writeln!(&mut toc, r#"

                <button id="toggle-nav">
                  Toggle navigation

                </button>

"#));
            let _ = write_toc(book, &item.path_to_root, &mut toc);
            try!(writeln!(&mut toc, "
"));
            try!(writeln!(&mut toc, "
"));
        }

        // write the postlude to a temporary HTML file for rustdoc inclusion
        let postlude = tmp.path().join("postlude.html");
        {
            let mut toc = BufWriter::new(try!(File::create(&postlude)));
            try!(toc.write_all(javascript::JAVASCRIPT.as_bytes()));
            try!(writeln!(&mut toc, "

"));
        }

        try!(fs::create_dir_all(&out_path));

        let rustdoc_args: &[String] = &[
            "".to_string(),
            preprocessed_path.display().to_string(),
            format!("-o{}", out_path.display()),
            format!("--html-before-content={}", prelude.display()),
            format!("--html-after-content={}", postlude.display()),
            format!("--markdown-playground-url=http://play.rust-lang.org"),
            format!("--markdown-css={}", item.path_to_root.join("rust-book.css").display()),
            "--markdown-no-toc".to_string(),
        ];
        let output_result = rustdoc::main_args(rustdoc_args);
        if output_result != 0 {
            let message = format!("Could not execute `rustdoc` with {:?}: {}",
                                  rustdoc_args, output_result);
            return Err(err(&message));
        }
    }

    // create index.html from the root README
    try!(fs::copy(&tgt.join("README.html"), &tgt.join("index.html")));

    // Copy some js for playpen
    let mut jquery = try!(File::create(tgt.join("jquery.js")));
    let js = include_bytes!("../librustdoc/html/static/jquery-2.1.0.min.js");
    try!(jquery.write_all(js));
    let mut playpen = try!(File::create(tgt.join("playpen.js")));
    let js = include_bytes!("../librustdoc/html/static/playpen.js");
    try!(playpen.write_all(js));
    Ok(())
}

impl Subcommand for Build {
    fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(&mut self, term: &mut Term) -> CommandResult<()> {
        let cwd = env::current_dir().unwrap();
        let src;
        let tgt;

        if env::args().len() < 3 {
            src = cwd.clone();
        } else {
            src = PathBuf::from(&env::args().nth(2).unwrap());
        }

        if env::args().len() < 4 {
            tgt = cwd.join("_book");
        } else {
            tgt = PathBuf::from(&env::args().nth(3).unwrap());
        }

        try!(fs::create_dir(&tgt));

        try!(File::create(&tgt.join("rust-book.css")).and_then(|mut f| {
            f.write_all(css::STYLE.as_bytes())
        }));

        let mut summary = try!(File::open(&src.join("SUMMARY.md")));
        match book::parse_summary(&mut summary, &src) {
            Ok(book) => {
                // execute rustdoc on the whole book
                render(&book, &tgt)
            }
            Err(errors) => {
                let n = errors.len();
                for err in errors {
                    term.err(&format!("error: {}", err)[..]);
                }

                Err(err(&format!("{} errors occurred", n)))
            }
        }
    }
}
<commit_msg>Rollup merge of #25354 - mdinger:delete_book, r=steveklabnik<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the `build` subcommand, used to compile a book.

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::path::{Path, PathBuf};
use rustc_back::tempdir::TempDir;

use subcommand::Subcommand;
use term::Term;
use error::{err, CliResult, CommandResult};
use book;
use book::{Book, BookItem};
use css;
use javascript;

use rustdoc;

struct Build;

pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
    if name == "build" {
        Some(Box::new(Build))
    } else {
        None
    }
}

fn write_toc(book: &Book, path_to_root: &Path, out: &mut Write) -> io::Result<()> {
    fn walk_items(items: &[BookItem],
                  section: &str,
                  path_to_root: &Path,
                  out: &mut Write) -> io::Result<()> {
        for (i, item) in items.iter().enumerate() {
            try!(walk_item(item, &format!("{}{}.", section, i + 1)[..], path_to_root, out));
        }
        Ok(())
    }
    fn walk_item(item: &BookItem,
                 section: &str,
                 path_to_root: &Path,
                 out: &mut Write) -> io::Result<()> {
        try!(writeln!(out, "
{} {}",
                 path_to_root.join(&item.path.with_extension("html")).display(),
                 section,
                 item.title));
        if !item.children.is_empty() {
            try!(writeln!(out, "
"));
            let _ = walk_items(&item.children[..], section, path_to_root, out);
            try!(writeln!(out, "
"));
        }
        try!(writeln!(out, "
"));

        Ok(())
    }

    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));
    try!(walk_items(&book.chapters[..], "", path_to_root, out));
    try!(writeln!(out, "
"));
    try!(writeln!(out, "
"));

    Ok(())
}

fn render(book: &Book, tgt: &Path) -> CliResult<()> {
    let tmp = try!(TempDir::new("rust-book"));

    for (_section, item) in book.iter() {
        let out_path = match item.path.parent() {
            Some(p) => tgt.join(p),
            None => tgt.to_path_buf(),
        };

        let src;
        if env::args().len() < 3 {
            src = env::current_dir().unwrap().clone();
        } else {
            src = PathBuf::from(&env::args().nth(2).unwrap());
        }
        // preprocess the markdown, rerouting markdown references to html
        // references
        let mut markdown_data = String::new();
        try!(File::open(&src.join(&item.path)).and_then(|mut f| {
            f.read_to_string(&mut markdown_data)
        }));
        let preprocessed_path = tmp.path().join(item.path.file_name().unwrap());
        {
            let urls = markdown_data.replace(".md)", ".html)");
            try!(File::create(&preprocessed_path).and_then(|mut f| {
                f.write_all(urls.as_bytes())
            }));
        }

        // write the prelude to a temporary HTML file for rustdoc inclusion
        let prelude = tmp.path().join("prelude.html");
        {
            let mut toc = BufWriter::new(try!(File::create(&prelude)));
            try!(writeln!(&mut toc, r#"

                <button id="toggle-nav">
                  Toggle navigation

                </button>

"#));
            let _ = write_toc(book, &item.path_to_root, &mut toc);
            try!(writeln!(&mut toc, "
"));
            try!(writeln!(&mut toc, "
"));
        }

        // write the postlude to a temporary HTML file for rustdoc inclusion
        let postlude = tmp.path().join("postlude.html");
        {
            let mut toc = BufWriter::new(try!(File::create(&postlude)));
            try!(toc.write_all(javascript::JAVASCRIPT.as_bytes()));
            try!(writeln!(&mut toc, "

"));
        }

        try!(fs::create_dir_all(&out_path));

        let rustdoc_args: &[String] = &[
            "".to_string(),
            preprocessed_path.display().to_string(),
            format!("-o{}", out_path.display()),
            format!("--html-before-content={}", prelude.display()),
            format!("--html-after-content={}", postlude.display()),
            format!("--markdown-playground-url=http://play.rust-lang.org"),
            format!("--markdown-css={}", item.path_to_root.join("rust-book.css").display()),
            "--markdown-no-toc".to_string(),
        ];
        let output_result = rustdoc::main_args(rustdoc_args);
        if output_result != 0 {
            let message = format!("Could not execute `rustdoc` with {:?}: {}",
                                  rustdoc_args, output_result);
            return Err(err(&message));
        }
    }

    // create index.html from the root README
    try!(fs::copy(&tgt.join("README.html"), &tgt.join("index.html")));

    // Copy some js for playpen
    let mut jquery = try!(File::create(tgt.join("jquery.js")));
    let js = include_bytes!("../librustdoc/html/static/jquery-2.1.0.min.js");
    try!(jquery.write_all(js));
    let mut playpen = try!(File::create(tgt.join("playpen.js")));
    let js = include_bytes!("../librustdoc/html/static/playpen.js");
    try!(playpen.write_all(js));
    Ok(())
}

impl Subcommand for Build {
    fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
        Ok(())
    }
    fn usage(&self) {}
    fn execute(&mut self, term: &mut Term) -> CommandResult<()> {
        let cwd = env::current_dir().unwrap();
        let src;
        let tgt;

        if env::args().len() < 3 {
            src = cwd.clone();
        } else {
            src = PathBuf::from(&env::args().nth(2).unwrap());
        }

        if env::args().len() < 4 {
            tgt = cwd.join("_book");
        } else {
            tgt = PathBuf::from(&env::args().nth(3).unwrap());
        }

        // `_book` directory may already exist from previous runs. Check and
        // delete it if it exists.
        for entry in try!(fs::read_dir(&cwd)) {
            let path = try!(entry).path();
            if path == tgt { try!(fs::remove_dir_all(&tgt)) }
        }
        try!(fs::create_dir(&tgt));

        try!(File::create(&tgt.join("rust-book.css")).and_then(|mut f| {
            f.write_all(css::STYLE.as_bytes())
        }));

        let mut summary = try!(File::open(&src.join("SUMMARY.md")));
        match book::parse_summary(&mut summary, &src) {
            Ok(book) => {
                // execute rustdoc on the whole book
                render(&book, &tgt)
            }
            Err(errors) => {
                let n = errors.len();
                for err in errors {
                    term.err(&format!("error: {}", err)[..]);
                }

                Err(err(&format!("{} errors occurred", n)))
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate alfred;

use config::Config;
use jira_api::SearchIssue;
use jira_client;
use search_response::SearchResponse;

struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<String, String> {
        let jira_search = SearchIssue{
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let issues = jira_client::send::<SearchIssue, SearchResponse>(jira_search).map_err(|e| e.to_string())?;
        Ok("".to_string())
    }
}<commit_msg>:art: Make possible to write alfred items<commit_after>extern crate alfred;

use config::Config;
use jira_api::SearchIssue;
use jira_client;
use search_response::SearchResponse;
use std::io::{self, Write};

pub struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<(), String> {
        let jira_search = SearchIssue{
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let res = jira_client::send::<SearchIssue, SearchResponse>(jira_search).map_err(|e| e.to_string())?;
        Ok(self.write_alfred_items(res))
    }

    fn write_alfred_items(&self, res: SearchResponse) {
        let alfred_items = res.issues.into_iter()
            .map(|issue| alfred::ItemBuilder::new(issue.key).arg(issue.url).into_item())
            .collect::<Vec<alfred::Item>>();

        alfred::json::write_items(io::stdout(), &alfred_items);
    }
}<|endoftext|>


<commit_before>use index::EntryKey;
use index::btree::NodeCellRef;
use index::Slice;
use index::btree::node::NodeWriteGuard;
use std::fmt::Debug;
use index::btree::node::write_node;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use std::mem;
use index::btree::node::EmptyNode;
use index::btree::node::write_key_page;
use index::btree::node::NodeReadHandler;
use index::btree::BPlusTree;
use index::btree::node::read_unchecked;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct RebalancingNodes<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    pub left_guard: NodeWriteGuard<KS, PS>,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard<KS, PS>,
    pub right_right_guard: NodeWriteGuard<KS, PS>, // for external pointer modification
pub parent: NodeWriteGuard<KS, PS>,
    pub parent_pos: usize,
}

pub struct RemoveResult<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    pub rebalancing: Option<RebalancingNodes<KS, PS>>,
    pub removed: bool,
}

pub fn with_innode_removing<F, KS, PS>(
    key: &EntryKey,
    mut rebalancing: RebalancingNodes<KS, PS>,
    parent: &NodeCellRef,
    parent_parent: &NodeCellRef,
    removed: bool,
    level: usize,
    func: F
) -> RemoveResult<KS, PS>
    where F: Fn(&mut RebalancingNodes<KS, PS>),
          KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let parent_half_full = rebalancing.parent.is_half_full();
    let mut parent_right_guard = write_node(&rebalancing.parent.innode_mut().right);
    let mut parent_remove_result = RemoveResult {
        rebalancing: None,
        removed
    };

    let parent_parent_guard;
    let parent_parent_remove_pos;
    let parent_right_right_guard;
    if level == 0 {
        parent_parent_guard = write_node(parent_parent);
        parent_right_right_guard = None;
        parent_parent_remove_pos = 0;
    } else {
        let pre_locked_parent_right_right_guard = parent_right_guard.right_ref_mut_no_empty().map(|r| write_node(r));
        let pp_guard = write_key_page(write_node(parent_parent), key);
        parent_parent_guard = pp_guard;
        parent_parent_remove_pos = parent_parent_guard.search(key);
        let parent_right_half_full = parent_right_guard.is_half_full();
        parent_right_right_guard = if parent_parent_guard.len() > parent_parent_remove_pos + 1 && (!parent_half_full || !parent_right_half_full) {
            // indicates whether the upper parent level need to relocated
            pre_locked_parent_right_right_guard
        } else { None };
    }

    func(&mut rebalancing);
    parent_remove_result.rebalancing = parent_right_right_guard.map(|parent_right_right_guard_stripped| {
        RebalancingNodes {
            left_guard: rebalancing.parent,
            left_ref: parent.clone(),
            right_right_guard: parent_right_right_guard_stripped,
            right_guard: parent_right_guard,
            parent: parent_parent_guard,
            parent_pos: parent_parent_remove_pos,
        }
    });
    parent_remove_result
}

pub fn remove_from_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node_ref: &mut NodeCellRef,
    key: &mut EntryKey,
    parent: &NodeCellRef,
    level: usize
) -> RemoveResult<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    debug!("Removing {:?} from node, level {}", key, level);
    let mut search = mut_search::<KS, PS>(node_ref, key);
    match search {
        MutSearchResult::Internal(mut sub_node) => {
            let mut node_remove_res = remove_from_node(tree,&mut sub_node, key, node_ref, level + 1);
            let removed = node_remove_res.removed;
            if let Some(mut rebalancing) = node_remove_res.rebalancing {
                debug!("Need to rebalance sub nodes, key {:?}, level {}", key, level);
                if rebalancing.left_guard.is_empty() && !rebalancing.right_guard.is_empty() {
                    debug!("Remove {:?} sub level left node is empty, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // Remove the empty node that have a right node with the same parent
                            // Because we cannot lock from left to right, we have to move the content
                            // of the right node to the left and remove the right node instead so the
                            // left right pointers can be modified
                            if !rebalancing.left_guard.is_empty() || (level == 0 && read_unchecked::<KS, PS>(&(*tree.root.read())).first_key() != rebalancing.left_guard.first_key()) {
                                return;
                            }

                            let mut left_node = &mut*rebalancing.left_guard;
                            let mut right_node = &mut*rebalancing.right_guard;
                            // swap the content of left and right, then delete right
                            // this procedure will prevent locking left node for changing right reference
                            let left_left_ref = left_node.left_ref_mut().map(|r| r.clone());
                            let right_right_ref = right_node.right_ref_mut_no_empty().map(|r| r.clone());
                            let left_ref = rebalancing.left_ref.clone();
                            // swap the empty node with the right node. In this case left node holds
                            // content of the right node but pointers need to be corrected.
                            mem::swap(left_node, right_node);
                            *right_node = NodeData::Empty(box EmptyNode { left: Some(left_ref.clone()), right: left_ref.clone() });
                            left_node.left_ref_mut().map(|r| *r = left_left_ref.unwrap());
                            left_node.right_ref_mut_no_empty().map(|r| *r = right_right_ref.unwrap());
                            rebalancing.right_right_guard.left_ref_mut().map(|r| *r = left_ref);
                            // remove the left ptr
                            rebalancing.parent.remove(rebalancing.parent_pos);
                            // point the right ptr to the replaced sub node
                            debug!("Removed sub level empty node, living node len {}, have {:?}", left_node.len(), left_node.keys());
                            rebalancing.parent.innode_mut().ptrs.as_slice()[rebalancing.parent_pos + 1] = sub_node.clone();
                        }))
                } else if rebalancing.right_guard.is_empty() {
                    debug!("Remove {:?} sub level right node is empty, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // There is a right empty node that can be deleted directly without hassle
                            let mut node_to_remove = &mut rebalancing.right_guard;
                            let owned_left_ref = rebalancing.left_ref.clone();
                            rebalancing.left_guard.right_ref_mut_no_empty().map(|r| *r = node_to_remove.right_ref_mut_no_empty().unwrap().clone());
                            rebalancing.right_right_guard.left_ref_mut().map(|r| *r = owned_left_ref);
                            rebalancing.parent.remove(rebalancing.parent_pos + 1);
                        }))
                } else if rebalancing.left_guard.cannot_merge() || rebalancing.right_guard.cannot_merge() {
                    if  rebalancing.right_guard.len() < KS::slice_len() / 3 &&
                        rebalancing.right_guard.len() < rebalancing.left_guard.len() &&
                        rebalancing.left_guard.len() > KS::slice_len() / 2 {
                        // Relocate the nodes with the same parent for balance.
                        // For OLFIT, items can only be located from left to right.
                        debug!("Remove {:?} sub level need relocation, level {}", key, level);
                        let mut left_node = &mut*rebalancing.left_guard;
                        let mut right_node = &mut*rebalancing.right_guard;
                        let left_pos = rebalancing.parent_pos;
                        let right_pos = left_pos + 1;
                        rebalancing.parent.innode_mut().relocate_children(left_pos, right_pos, left_node, right_node);
                    }
                    None
                } else if rebalancing.left_guard.len() + rebalancing.right_guard.len() + 1 <= KS::slice_len() {
                    // Nodes with the same parent can merge
                    debug!("Remove {:?} sub level need to be merged, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            let mut left_node = &mut *rebalancing.left_guard;
                            let mut right_node = &mut *rebalancing.right_guard;
                            let mut right_node_next = &mut *rebalancing.right_right_guard;
                            let left_pos = rebalancing.parent_pos;
                            let right_pos = left_pos + 1;
                            if left_node.is_empty() || right_node.is_empty() { return; }
                            rebalancing.parent.innode_mut().merge_children(left_pos, right_pos, left_node, right_node, right_node_next);
                        }))
                    // None
                } else if rebalancing.parent.len() == 1 && rebalancing.left_guard.is_empty() && rebalancing.right_guard.is_empty() {
                    // this node and its children is empty, should be removed
                    debug!("Cleaning up empty level nodes, level {}", level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level, |rebalancing| {
                            rebalancing.parent.remove(0);
                            rebalancing.parent.innode_mut().ptrs.as_slice()[0] = Default::default();
                        }))
                } else {
                    None
                }
            } else {
                None
            }.unwrap_or_else(|| {
                RemoveResult {
                    rebalancing: None,
                    removed
                }
            })
        }
        MutSearchResult::External => {
            let node_guard: NodeWriteGuard<KS, PS> = write_node(node_ref);
            let mut target_guard = write_key_page(node_guard, key);
            let target_guard_ref = target_guard.node_ref().clone();
            let mut remove_result = RemoveResult {
                rebalancing: None,
                removed: false
            };
            {
                let is_left_half_full = target_guard.is_half_full();
                let mut node = target_guard.extnode_mut();
                let pos = node.search(key);
                let mut right_guard = write_node(&node.next);
                let right_node_cannot_rebalance = right_guard.is_none() || !right_guard.is_empty_node();
                if !right_guard.is_none() && !is_left_half_full || !right_guard.is_half_full(){
                    let right_right_guard = write_node(&right_guard.right_ref_mut_no_empty().unwrap());
                    let parent_guard = write_node(parent);
                    let parent_target_guard = write_key_page(parent_guard, key);
                    let parent_pos = parent_target_guard.search(key);
                    // Check if the right node is innode and its parent is the same as the left one
                    // because we have to lock from left to right, there is no way to lock backwards
                    // if the empty non half-full node is at the right most of its parent
                    // So left over imbalanced such nodes will be expected and they can be eliminate
                    // by their left node remove operations.
                    if parent_pos < parent_target_guard.len() - 1 {
                        debug!("removing innode have a rebalance requirement");
                        remove_result.rebalancing = Some(RebalancingNodes {
                            left_ref: target_guard_ref,
                            left_guard: Default::default(),
                            parent: parent_target_guard,
                            parent_pos,
                            right_right_guard,
                            right_guard
                        });
                    } else {
                        debug!("removing innode have a rebalance requirement but left-right node does not with the same parent");
                    }
                }
                if pos >= node.len {
                    debug!(
                        "Removing pos overflows external node, pos {}, len {}, expecting key {:?}, current keys {:?}, right keys {:?}",
                        pos, node.len, key, node.keys, read_unchecked::<KS, PS>(&node.next).keys()
                    );
                    remove_result.removed = false;
                }
                if &node.keys.as_slice()[pos] == key {
                    debug!("Removing key {:?} at {}, keys {:?}", key, pos, &node.keys);
                    node.remove_at(pos);
                    remove_result.removed = true;
                } else {
                    debug!(
                        "Search check failed for remove at pos {}, expecting {:?}, actual {:?}, have {:?}",
                        pos, key, &node.keys.as_slice_immute()[pos], &node.keys
                    );
                    remove_result.removed = false;
                }
            }
            if let Some(ref mut rebalance) = &mut remove_result.rebalancing {
                rebalance.left_guard = target_guard;
            }
            remove_result
        }
    }
}<commit_msg>comment on unstable remove<commit_after>// This function is unstable yet, tests ignored

use index::EntryKey;
use index::btree::NodeCellRef;
use index::Slice;
use index::btree::node::NodeWriteGuard;
use std::fmt::Debug;
use index::btree::node::write_node;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use std::mem;
use index::btree::node::EmptyNode;
use index::btree::node::write_key_page;
use index::btree::node::NodeReadHandler;
use index::btree::BPlusTree;
use index::btree::node::read_unchecked;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct RebalancingNodes<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    pub left_guard: NodeWriteGuard<KS, PS>,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard<KS, PS>,
    pub right_right_guard: NodeWriteGuard<KS, PS>, // for external pointer modification
pub parent: NodeWriteGuard<KS, PS>,
    pub parent_pos: usize,
}

pub struct RemoveResult<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    pub rebalancing: Option<RebalancingNodes<KS, PS>>,
    pub removed: bool,
}

pub fn with_innode_removing<F, KS, PS>(
    key: &EntryKey,
    mut rebalancing: RebalancingNodes<KS, PS>,
    parent: &NodeCellRef,
    parent_parent: &NodeCellRef,
    removed: bool,
    level: usize,
    func: F
) -> RemoveResult<KS, PS>
    where F: Fn(&mut RebalancingNodes<KS, PS>),
          KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let parent_half_full = rebalancing.parent.is_half_full();
    let mut parent_right_guard = write_node(&rebalancing.parent.innode_mut().right);
    let mut parent_remove_result = RemoveResult {
        rebalancing: None,
        removed
    };

    let parent_parent_guard;
    let parent_parent_remove_pos;
    let parent_right_right_guard;
    if level == 0 {
        parent_parent_guard = write_node(parent_parent);
        parent_right_right_guard = None;
        parent_parent_remove_pos = 0;
    } else {
        let pre_locked_parent_right_right_guard = parent_right_guard.right_ref_mut_no_empty().map(|r| write_node(r));
        let pp_guard = write_key_page(write_node(parent_parent), key);
        parent_parent_guard = pp_guard;
        parent_parent_remove_pos = parent_parent_guard.search(key);
        let parent_right_half_full = parent_right_guard.is_half_full();
        parent_right_right_guard = if parent_parent_guard.len() > parent_parent_remove_pos + 1 && (!parent_half_full || !parent_right_half_full) {
            // indicates whether the upper parent level need to relocated
            pre_locked_parent_right_right_guard
        } else { None };
    }

    func(&mut rebalancing);
    parent_remove_result.rebalancing = parent_right_right_guard.map(|parent_right_right_guard_stripped| {
        RebalancingNodes {
            left_guard: rebalancing.parent,
            left_ref: parent.clone(),
            right_right_guard: parent_right_right_guard_stripped,
            right_guard: parent_right_guard,
            parent: parent_parent_guard,
            parent_pos: parent_parent_remove_pos,
        }
    });
    parent_remove_result
}

pub fn remove_from_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node_ref: &mut NodeCellRef,
    key: &mut EntryKey,
    parent: &NodeCellRef,
    level: usize
) -> RemoveResult<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    debug!("Removing {:?} from node, level {}", key, level);
    let mut search = mut_search::<KS, PS>(node_ref, key);
    match search {
        MutSearchResult::Internal(mut sub_node) => {
            let mut node_remove_res = remove_from_node(tree,&mut sub_node, key, node_ref, level + 1);
            let removed = node_remove_res.removed;
            if let Some(mut rebalancing) = node_remove_res.rebalancing {
                debug!("Need to rebalance sub nodes, key {:?}, level {}", key, level);
                if rebalancing.left_guard.is_empty() && !rebalancing.right_guard.is_empty() {
                    debug!("Remove {:?} sub level left node is empty, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // Remove the empty node that have a right node with the same parent
                            // Because we cannot lock from left to right, we have to move the content
                            // of the right node to the left and remove the right node instead so the
                            // left right pointers can be modified
                            if !rebalancing.left_guard.is_empty() || (level == 0 && read_unchecked::<KS, PS>(&(*tree.root.read())).first_key() != rebalancing.left_guard.first_key()) {
                                return;
                            }

                            let mut left_node = &mut*rebalancing.left_guard;
                            let mut right_node = &mut*rebalancing.right_guard;
                            // swap the content of left and right, then delete right
                            // this procedure will prevent locking left node for changing right reference
                            let left_left_ref = left_node.left_ref_mut().map(|r| r.clone());
                            let right_right_ref = right_node.right_ref_mut_no_empty().map(|r| r.clone());
                            let left_ref = rebalancing.left_ref.clone();
                            // swap the empty node with the right node. In this case left node holds
                            // content of the right node but pointers need to be corrected.
                            mem::swap(left_node, right_node);
                            *right_node = NodeData::Empty(box EmptyNode { left: Some(left_ref.clone()), right: left_ref.clone() });
                            left_node.left_ref_mut().map(|r| *r = left_left_ref.unwrap());
                            left_node.right_ref_mut_no_empty().map(|r| *r = right_right_ref.unwrap());
                            rebalancing.right_right_guard.left_ref_mut().map(|r| *r = left_ref);
                            // remove the left ptr
                            rebalancing.parent.remove(rebalancing.parent_pos);
                            // point the right ptr to the replaced sub node
                            debug!("Removed sub level empty node, living node len {}, have {:?}", left_node.len(), left_node.keys());
                            rebalancing.parent.innode_mut().ptrs.as_slice()[rebalancing.parent_pos + 1] = sub_node.clone();
                        }))
                } else if rebalancing.right_guard.is_empty() {
                    debug!("Remove {:?} sub level right node is empty, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // There is a right empty node that can be deleted directly without hassle
                            let mut node_to_remove = &mut rebalancing.right_guard;
                            let owned_left_ref = rebalancing.left_ref.clone();
                            rebalancing.left_guard.right_ref_mut_no_empty().map(|r| *r = node_to_remove.right_ref_mut_no_empty().unwrap().clone());
                            rebalancing.right_right_guard.left_ref_mut().map(|r| *r = owned_left_ref);
                            rebalancing.parent.remove(rebalancing.parent_pos + 1);
                        }))
                } else if rebalancing.left_guard.cannot_merge() || rebalancing.right_guard.cannot_merge() {
                    if  rebalancing.right_guard.len() < KS::slice_len() / 3 &&
                        rebalancing.right_guard.len() < rebalancing.left_guard.len() &&
                        rebalancing.left_guard.len() > KS::slice_len() / 2 {
                        // Relocate the nodes with the same parent for balance.
                        // For OLFIT, items can only be located from left to right.
                        debug!("Remove {:?} sub level need relocation, level {}", key, level);
                        let mut left_node = &mut*rebalancing.left_guard;
                        let mut right_node = &mut*rebalancing.right_guard;
                        let left_pos = rebalancing.parent_pos;
                        let right_pos = left_pos + 1;
                        rebalancing.parent.innode_mut().relocate_children(left_pos, right_pos, left_node, right_node);
                    }
                    None
                } else if rebalancing.left_guard.len() + rebalancing.right_guard.len() + 1 <= KS::slice_len() {
                    // Nodes with the same parent can merge
                    debug!("Remove {:?} sub level need to be merged, level {}", key, level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            let mut left_node = &mut *rebalancing.left_guard;
                            let mut right_node = &mut *rebalancing.right_guard;
                            let mut right_node_next = &mut *rebalancing.right_right_guard;
                            let left_pos = rebalancing.parent_pos;
                            let right_pos = left_pos + 1;
                            if left_node.is_empty() || right_node.is_empty() { return; }
                            rebalancing.parent.innode_mut().merge_children(left_pos, right_pos, left_node, right_node, right_node_next);
                        }))
                    // None
                } else if rebalancing.parent.len() == 1 && rebalancing.left_guard.is_empty() && rebalancing.right_guard.is_empty() {
                    // this node and its children is empty, should be removed
                    debug!("Cleaning up empty level nodes, level {}", level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level, |rebalancing| {
                            rebalancing.parent.remove(0);
                            rebalancing.parent.innode_mut().ptrs.as_slice()[0] = Default::default();
                        }))
                } else {
                    None
                }
            } else {
                None
            }.unwrap_or_else(|| {
                RemoveResult {
                    rebalancing: None,
                    removed
                }
            })
        }
        MutSearchResult::External => {
            let node_guard: NodeWriteGuard<KS, PS> = write_node(node_ref);
            let mut target_guard = write_key_page(node_guard, key);
            let target_guard_ref = target_guard.node_ref().clone();
            let mut remove_result = RemoveResult {
                rebalancing: None,
                removed: false
            };
            {
                let is_left_half_full = target_guard.is_half_full();
                let mut node = target_guard.extnode_mut();
                let pos = node.search(key);
                let mut right_guard = write_node(&node.next);
                let right_node_cannot_rebalance = right_guard.is_none() || !right_guard.is_empty_node();
                if !right_guard.is_none() && !is_left_half_full || !right_guard.is_half_full(){
                    let right_right_guard = write_node(&right_guard.right_ref_mut_no_empty().unwrap());
                    let parent_guard = write_node(parent);
                    let parent_target_guard = write_key_page(parent_guard, key);
                    let parent_pos = parent_target_guard.search(key);
                    // Check if the right node is innode and its parent is the same as the left one
                    // because we have to lock from left to right, there is no way to lock backwards
                    // if the empty non half-full node is at the right most of its parent
                    // So left over imbalanced such nodes will be expected and they can be eliminate
                    // by their left node remove operations.
                    if parent_pos < parent_target_guard.len() - 1 {
                        debug!("removing innode have a rebalance requirement");
                        remove_result.rebalancing = Some(RebalancingNodes {
                            left_ref: target_guard_ref,
                            left_guard: Default::default(),
                            parent: parent_target_guard,
                            parent_pos,
                            right_right_guard,
                            right_guard
                        });
                    } else {
                        debug!("removing innode have a rebalance requirement but left-right node does not with the same parent");
                    }
                }
                if pos >= node.len {
                    debug!(
                        "Removing pos overflows external node, pos {}, len {}, expecting key {:?}, current keys {:?}, right keys {:?}",
                        pos, node.len, key, node.keys, read_unchecked::<KS, PS>(&node.next).keys()
                    );
                    remove_result.removed = false;
                }
                if &node.keys.as_slice()[pos] == key {
                    debug!("Removing key {:?} at {}, keys {:?}", key, pos, &node.keys);
                    node.remove_at(pos);
                    remove_result.removed = true;
                } else {
                    debug!(
                        "Search check failed for remove at pos {}, expecting {:?}, actual {:?}, have {:?}",
                        pos, key, &node.keys.as_slice_immute()[pos], &node.keys
                    );
                    remove_result.removed = false;
                }
            }
            if let Some(ref mut rebalance) = &mut remove_result.rebalancing {
                rebalance.left_guard = target_guard;
            }
            remove_result
        }
    }
}<|endoftext|>


<commit_before>use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::MemoryType;
use interpreter::Error;
use interpreter::module::check_limits;

/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;

/// Linear memory instance.
pub struct MemoryInstance {
	/// Linear memory buffer.
	buffer: RwLock<Vec<u8>>,
	/// Maximum buffer size.
	maximum_size: u32,
}

impl MemoryInstance {
	/// Create new linear memory instance.
	pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error> {
		check_limits(memory_type.limits())?;

		if let Some(maximum_pages) = memory_type.limits().maximum() {
			if maximum_pages > LINEAR_MEMORY_MAX_PAGES {
				return Err(Error::Memory(format!("memory size must be at most 65536 pages")));
			}
		}

		let memory = MemoryInstance {
			buffer: RwLock::new(Vec::new()), // TODO: with_capacity
			maximum_size: memory_type.limits().maximum()
				.map(|s| s.saturating_mul(LINEAR_MEMORY_PAGE_SIZE))
				.unwrap_or(u32::MAX),
		};
		if memory.grow(memory_type.limits().initial())? == u32::MAX {
			return Err(Error::Memory(format!("error initializing {}-pages linear memory region", memory_type.limits().initial())));
		}
		Ok(Arc::new(memory))
	}

	/// Return linear memory size (in pages).
	pub fn size(&self) -> u32 {
		self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
	}

	/// Get data at given offset.
	pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
		let begin = offset as usize;
		let end = match begin.checked_add(size) {
			Some(end) => end,
			None => return Err(Error::Memory(format!("trying to read memory block of size {} from offset {}", size, offset))),
		};

		let buffer = self.buffer.read();
		if buffer.len() < end {
			return Err(Error::Memory(format!("trying to read region [{}..{}] in memory [0..{}]", begin, end, buffer.len())));
		}

		Ok(buffer[begin..end].to_vec())
	}

	/// Set data at given offset.
	pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
		let size = value.len();
		let begin = offset as usize;
		let end = match begin.checked_add(size) {
			Some(end) => end,
			None => return Err(Error::Memory(format!("trying to update memory block of size {} from offset {}", size, offset))),
		};

		let mut buffer = self.buffer.write();
		if buffer.len() < end {
			return Err(Error::Memory(format!("trying to update region [{}..{}] in memory [0..{}]", begin, end, buffer.len())));
		}

		let mut mut_buffer = buffer.as_mut_slice();
		mut_buffer[begin..end].copy_from_slice(value);

		Ok(())
	}

	/// Increases the size of the linear memory by given number of pages.
	/// Returns -1 if allocation fails or previous memory size, if succeeds.
	pub fn grow(&self, pages: u32) -> Result<u32, Error> {
		let mut buffer = self.buffer.write();
		let old_size = buffer.len() as u32;
		match pages.checked_mul(LINEAR_MEMORY_PAGE_SIZE).and_then(|bytes| old_size.checked_add(bytes)) {
			None => Ok(u32::MAX),
			Some(new_size) if new_size > self.maximum_size => Ok(u32::MAX),
			Some(new_size) => {
				buffer.extend(vec![0; (new_size - old_size) as usize]);
				Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
			},
		}
	}
}
<commit_msg>memory upgrade<commit_after>use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::MemoryType;
use interpreter::Error;
use interpreter::module::check_limits;

/// Linear memory page size.
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Maximal number of pages.
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;

/// Linear memory instance.
pub struct MemoryInstance {
	/// Linear memory buffer.
	buffer: RwLock<Vec<u8>>,
	/// Maximum buffer size.
	maximum_size: u32,
}

struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref<Target=Vec<u8>> {
	_buffer: &'a B,
	offset: usize,
	size: usize,
}

impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>> {
	fn range(&self) -> ::std::ops::Range<usize> {
		self.offset..self.offset+self.size
	}
}

impl MemoryInstance {
	/// Create new linear memory instance.
	pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error> {
		check_limits(memory_type.limits())?;

		if let Some(maximum_pages) = memory_type.limits().maximum() {
			if maximum_pages > LINEAR_MEMORY_MAX_PAGES {
				return Err(Error::Memory(format!("memory size must be at most 65536 pages")));
			}
		}

		let memory = MemoryInstance {
			buffer: RwLock::new(Vec::new()), // TODO: with_capacity
			maximum_size: memory_type.limits().maximum()
				.map(|s| s.saturating_mul(LINEAR_MEMORY_PAGE_SIZE))
				.unwrap_or(u32::MAX),
		};
		if memory.grow(memory_type.limits().initial())? == u32::MAX {
			return Err(Error::Memory(format!("error initializing {}-pages linear memory region", memory_type.limits().initial())));
		}
		Ok(Arc::new(memory))
	}

	/// Return linear memory size (in pages).
	pub fn size(&self) -> u32 {
		self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
	}

	/// Get data at given offset.
	pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
		let buffer = self.buffer.read();
		let region = self.checked_region(&buffer, offset as usize, size)?;

		Ok(buffer[region.range()].to_vec())
	}

	/// Set data at given offset.
	pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
		let mut buffer = self.buffer.write();
		let range = self.checked_region(&buffer, offset as usize, value.len())?.range();

		buffer[range].copy_from_slice(value);

		Ok(())
	}

	/// Increases the size of the linear memory by given number of pages.
	/// Returns -1 if allocation fails or previous memory size, if succeeds.
	pub fn grow(&self, pages: u32) -> Result<u32, Error> {
		let mut buffer = self.buffer.write();
		let old_size = buffer.len() as u32;
		match pages.checked_mul(LINEAR_MEMORY_PAGE_SIZE).and_then(|bytes| old_size.checked_add(bytes)) {
			None => Ok(u32::MAX),
			Some(new_size) if new_size > self.maximum_size => Ok(u32::MAX),
			Some(new_size) => {
				buffer.extend(vec![0; (new_size - old_size) as usize]);
				Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
			},
		}
	}

	fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error>
		where B: ::std::ops::Deref<Target=Vec<u8>>
	{
		let end = offset.checked_add(size)
			.ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?;

		if end > buffer.len() {
			return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len())));
		}

		Ok(CheckedRegion {
			_buffer: buffer,
			offset: offset,
			size: size,
		})
	}

	pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> {
		let buffer = self.buffer.write();

		let read_region = self.checked_region(&buffer, src_offset, len)?;
		let write_region = self.checked_region(&buffer, dst_offset, len)?;

		unsafe { ::std::ptr::copy(
			buffer[read_region.range()].as_ptr(),
			buffer[write_region.range()].as_ptr() as *mut _,
			len,
		)}

		Ok(())
	}
}
<|endoftext|>


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

/*!

Synchronous Timers

This module exposes the functionality to create timers, block the current task,
and create ports which will receive notifications after a period of time.

# Example

```rust

use std::rt::io::Timer;

let mut timer = Timer::new().unwrap();
timer.sleep(10); // block the task for awhile

let timeout = timer.oneshot(10);
// do some work
timeout.recv(); // wait for the timeout to expire

let periodic = timer.periodic(10);
loop {
    periodic.recv();
    // this loop is only executed once every 10ms
}

```

*/

use comm::{Port, PortOne};
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::io_error;
use rt::rtio::{IoFactory, RtioTimer, with_local_io};

pub struct Timer {
    priv obj: ~RtioTimer
}

/// Sleep the current task for `msecs` milliseconds.
pub fn sleep(msecs: u64) {
    let mut timer = Timer::new().expect("timer::sleep: could not create a Timer");

    timer.sleep(msecs)
}

impl Timer {
    /// Creates a new timer which can be used to put the current task to sleep
    /// for a number of milliseconds, or to possibly create channels which will
    /// get notified after an amount of time has passed.
    pub fn new() -> Option<Timer> {
        do with_local_io |io| {
            match io.timer_init() {
                Ok(t) => Some(Timer { obj: t }),
                Err(ioerr) => {
                    rtdebug!("Timer::init: failed to init: {:?}", ioerr);
                    io_error::cond.raise(ioerr);
                    None
                }
            }

        }
    }

    /// Blocks the current task for `msecs` milliseconds.
    ///
    /// Note that this function will cause any other ports for this timer to be
    /// invalidated (the other end will be closed).
    pub fn sleep(&mut self, msecs: u64) {
        self.obj.sleep(msecs);
    }

    /// Creates a oneshot port which will have a notification sent when `msecs`
    /// milliseconds has elapsed. This does *not* block the current task, but
    /// instead returns immediately.
    ///
    /// Note that this invalidates any previous port which has been created by
    /// this timer, and that the returned port will be invalidated once the
    /// timer is destroyed (when it falls out of scope).
    pub fn oneshot(&mut self, msecs: u64) -> PortOne<()> {
        self.obj.oneshot(msecs)
    }

    /// Creates a port which will have a continuous stream of notifications
    /// being sent every `msecs` milliseconds. This does *not* block the
    /// current task, but instead returns immediately. The first notification
    /// will not be received immediately, but rather after `msec` milliseconds
    /// have passed.
    ///
    /// Note that this invalidates any previous port which has been created by
    /// this timer, and that the returned port will be invalidated once the
    /// timer is destroyed (when it falls out of scope).
    pub fn periodic(&mut self, msecs: u64) -> Port<()> {
        self.obj.period(msecs)
    }
}

#[cfg(test)]
mod test {
    use prelude::*;
    use super::*;
    use rt::test::*;
    use cell::Cell;
    use task;

    #[test]
    fn test_io_timer_sleep_simple() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.sleep(1);
        }
    }

    #[test]
    fn test_io_timer_sleep_oneshot() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.oneshot(1).recv();
        }
    }

    #[test]
    fn test_io_timer_sleep_oneshot_forget() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.oneshot(100000000000);
        }
    }

    #[test]
    fn oneshot_twice() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port1 = timer.oneshot(10000);
            let port = timer.oneshot(1);
            port.recv();
            assert_eq!(port1.try_recv(), None);
        }
    }

    #[test]
    fn test_io_timer_oneshot_then_sleep() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port = timer.oneshot(100000000000);
            timer.sleep(1); // this should invalidate the port

            assert_eq!(port.try_recv(), None);
        }
    }

    #[test]
    fn test_io_timer_sleep_periodic() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port = timer.periodic(1);
            port.recv();
            port.recv();
            port.recv();
        }
    }

    #[test]
    fn test_io_timer_sleep_periodic_forget() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.periodic(100000000000);
        }
    }

    #[test]
    fn test_io_timer_sleep_standalone() {
        do run_in_mt_newsched_task {
            sleep(1)
        }
    }
}
<commit_msg>Clean lint on test build<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

Synchronous Timers

This module exposes the functionality to create timers, block the current task,
and create ports which will receive notifications after a period of time.

# Example

```rust

use std::rt::io::Timer;

let mut timer = Timer::new().unwrap();
timer.sleep(10); // block the task for awhile

let timeout = timer.oneshot(10);
// do some work
timeout.recv(); // wait for the timeout to expire

let periodic = timer.periodic(10);
loop {
    periodic.recv();
    // this loop is only executed once every 10ms
}

```

*/

use comm::{Port, PortOne};
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::io_error;
use rt::rtio::{IoFactory, RtioTimer, with_local_io};

pub struct Timer {
    priv obj: ~RtioTimer
}

/// Sleep the current task for `msecs` milliseconds.
pub fn sleep(msecs: u64) {
    let mut timer = Timer::new().expect("timer::sleep: could not create a Timer");

    timer.sleep(msecs)
}

impl Timer {
    /// Creates a new timer which can be used to put the current task to sleep
    /// for a number of milliseconds, or to possibly create channels which will
    /// get notified after an amount of time has passed.
    pub fn new() -> Option<Timer> {
        do with_local_io |io| {
            match io.timer_init() {
                Ok(t) => Some(Timer { obj: t }),
                Err(ioerr) => {
                    rtdebug!("Timer::init: failed to init: {:?}", ioerr);
                    io_error::cond.raise(ioerr);
                    None
                }
            }

        }
    }

    /// Blocks the current task for `msecs` milliseconds.
    ///
    /// Note that this function will cause any other ports for this timer to be
    /// invalidated (the other end will be closed).
    pub fn sleep(&mut self, msecs: u64) {
        self.obj.sleep(msecs);
    }

    /// Creates a oneshot port which will have a notification sent when `msecs`
    /// milliseconds has elapsed. This does *not* block the current task, but
    /// instead returns immediately.
    ///
    /// Note that this invalidates any previous port which has been created by
    /// this timer, and that the returned port will be invalidated once the
    /// timer is destroyed (when it falls out of scope).
    pub fn oneshot(&mut self, msecs: u64) -> PortOne<()> {
        self.obj.oneshot(msecs)
    }

    /// Creates a port which will have a continuous stream of notifications
    /// being sent every `msecs` milliseconds. This does *not* block the
    /// current task, but instead returns immediately. The first notification
    /// will not be received immediately, but rather after `msec` milliseconds
    /// have passed.
    ///
    /// Note that this invalidates any previous port which has been created by
    /// this timer, and that the returned port will be invalidated once the
    /// timer is destroyed (when it falls out of scope).
    pub fn periodic(&mut self, msecs: u64) -> Port<()> {
        self.obj.period(msecs)
    }
}

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

    #[test]
    fn test_io_timer_sleep_simple() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.sleep(1);
        }
    }

    #[test]
    fn test_io_timer_sleep_oneshot() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.oneshot(1).recv();
        }
    }

    #[test]
    fn test_io_timer_sleep_oneshot_forget() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.oneshot(100000000000);
        }
    }

    #[test]
    fn oneshot_twice() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port1 = timer.oneshot(10000);
            let port = timer.oneshot(1);
            port.recv();
            assert_eq!(port1.try_recv(), None);
        }
    }

    #[test]
    fn test_io_timer_oneshot_then_sleep() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port = timer.oneshot(100000000000);
            timer.sleep(1); // this should invalidate the port

            assert_eq!(port.try_recv(), None);
        }
    }

    #[test]
    fn test_io_timer_sleep_periodic() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            let port = timer.periodic(1);
            port.recv();
            port.recv();
            port.recv();
        }
    }

    #[test]
    fn test_io_timer_sleep_periodic_forget() {
        do run_in_mt_newsched_task {
            let mut timer = Timer::new().unwrap();
            timer.periodic(100000000000);
        }
    }

    #[test]
    fn test_io_timer_sleep_standalone() {
        do run_in_mt_newsched_task {
            sleep(1)
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_chown"]
// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown, uid_t};
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::fs::{self, Metadata};
use std::os::unix::fs::MetadataExt;

use std::io;
use std::io::Result as IOResult;

use std::path::Path;
use std::convert::AsRef;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &str =
    "[OPTION]... [OWNER][:[GROUP]] FILE...\n chown [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &str = "change file owner and group";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")

        .optopt("", "from", "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute", "CURRENT_OWNER:CURRENT_GROUP")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")

        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    let filter = if let Some(spec) = matches.opt_str("from") {
        match parse_spec(&spec) {
            Ok((Some(uid), None)) => IfFrom::User(uid),
            Ok((None, Some(gid))) => IfFrom::Group(gid),
            Ok((Some(uid), Some(gid))) => IfFrom::UserGroup(uid, gid),
            Ok((None, None)) => IfFrom::All,
            Err(e) => {
                show_info!("{}", e);
                return 1;
            }
        }
    } else {
        IfFrom::All
    };

    if matches.free.is_empty() {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let dest_uid: Option<u32>;
    let dest_gid: Option<u32>;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = Some(meta.gid());
                dest_uid = Some(meta.uid());
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
    } else {
        match parse_spec(&matches.free[0]) {
            Ok((u, g)) => {
                dest_uid = u;
                dest_gid = g;
            }
            Err(e) => {
                show_info!("{}", e);
                return 1;
            }
        }
    }
    let mut files = matches.free;
    files.remove(0);
    let executor = Chowner {
        bit_flag,
        dest_uid,
        dest_gid,
        verbosity,
        recursive,
        dereference: derefer != 0,
        filter,
        preserve_root,
        files,
    };
    executor.exec()
}

fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
    let args = spec.split(':').collect::<Vec<_>>();
    let usr_only = args.len() == 1;
    let grp_only = args.len() == 2 && args[0].is_empty() && !args[1].is_empty();
    let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();

    if usr_only {
        Ok((
            Some(match Passwd::locate(args[0]) {
                Ok(v) => v.uid(),
                _ => return Err(format!("invalid user: ‘{}’", spec)),
            }),
            None,
        ))
    } else if grp_only {
        Ok((
            None,
            Some(match Group::locate(args[1]) {
                Ok(v) => v.gid(),
                _ => return Err(format!("invalid group: ‘{}’", spec)),
            }),
        ))
    } else if usr_grp {
        Ok((
            Some(match Passwd::locate(args[0]) {
                Ok(v) => v.uid(),
                _ => return Err(format!("invalid user: ‘{}’", spec)),
            }),
            Some(match Group::locate(args[1]) {
                Ok(v) => v.gid(),
                _ => return Err(format!("invalid group: ‘{}’", spec)),
            }),
        ))
    } else {
        Ok((None, None))
    }
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

struct Chowner {
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    bit_flag: u8,
    verbosity: Verbosity,
    filter: IfFrom,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chowner {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chown<P: AsRef<Path>>(
        &self,
        path: P,
        duid: uid_t,
        dgid: gid_t,
        follow: bool,
    ) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), duid, dgid)
            } else {
                lchown(s.as_ptr(), duid, dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            self.wrap_chown(path, &meta, follow_arg)
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = self.wrap_chown(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chown<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_uid = self.dest_uid.unwrap_or(meta.uid());
        let dest_gid = self.dest_gid.unwrap_or(meta.gid());
        let path = path.as_ref();
        if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing ownership of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!(
                            "failed to change ownership of {} from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        );
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!(
                            "changed ownership of {} from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        );
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!(
                    "ownership of {} retained as {}:{}",
                    path.display(),
                    entries::uid2usr(dest_uid).unwrap(),
                    entries::gid2grp(dest_gid).unwrap()
                );
            }
        }
        ret
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}
<commit_msg>chown: change first file with --reference<commit_after>#![crate_name = "uu_chown"]
// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown, uid_t};
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::fs::{self, Metadata};
use std::os::unix::fs::MetadataExt;

use std::io;
use std::io::Result as IOResult;

use std::path::Path;
use std::convert::AsRef;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &str =
    "[OPTION]... [OWNER][:[GROUP]] FILE...\n chown [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &str = "change file owner and group";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")

        .optopt("", "from", "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute", "CURRENT_OWNER:CURRENT_GROUP")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")

        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    let filter = if let Some(spec) = matches.opt_str("from") {
        match parse_spec(&spec) {
            Ok((Some(uid), None)) => IfFrom::User(uid),
            Ok((None, Some(gid))) => IfFrom::Group(gid),
            Ok((Some(uid), Some(gid))) => IfFrom::UserGroup(uid, gid),
            Ok((None, None)) => IfFrom::All,
            Err(e) => {
                show_info!("{}", e);
                return 1;
            }
        }
    } else {
        IfFrom::All
    };

    if matches.free.is_empty() {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let mut files;
    let dest_uid: Option<u32>;
    let dest_gid: Option<u32>;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = Some(meta.gid());
                dest_uid = Some(meta.uid());
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
        files = matches.free;
    } else {
        match parse_spec(&matches.free[0]) {
            Ok((u, g)) => {
                dest_uid = u;
                dest_gid = g;
            }
            Err(e) => {
                show_info!("{}", e);
                return 1;
            }
        }
        files = matches.free;
        files.remove(0);
    }
    let executor = Chowner {
        bit_flag,
        dest_uid,
        dest_gid,
        verbosity,
        recursive,
        dereference: derefer != 0,
        filter,
        preserve_root,
        files,
    };
    executor.exec()
}

fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
    let args = spec.split(':').collect::<Vec<_>>();
    let usr_only = args.len() == 1;
    let grp_only = args.len() == 2 && args[0].is_empty() && !args[1].is_empty();
    let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();

    if usr_only {
        Ok((
            Some(match Passwd::locate(args[0]) {
                Ok(v) => v.uid(),
                _ => return Err(format!("invalid user: ‘{}’", spec)),
            }),
            None,
        ))
    } else if grp_only {
        Ok((
            None,
            Some(match Group::locate(args[1]) {
                Ok(v) => v.gid(),
                _ => return Err(format!("invalid group: ‘{}’", spec)),
            }),
        ))
    } else if usr_grp {
        Ok((
            Some(match Passwd::locate(args[0]) {
                Ok(v) => v.uid(),
                _ => return Err(format!("invalid user: ‘{}’", spec)),
            }),
            Some(match Group::locate(args[1]) {
                Ok(v) => v.gid(),
                _ => return Err(format!("invalid group: ‘{}’", spec)),
            }),
        ))
    } else {
        Ok((None, None))
    }
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

struct Chowner {
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    bit_flag: u8,
    verbosity: Verbosity,
    filter: IfFrom,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chowner {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chown<P: AsRef<Path>>(
        &self,
        path: P,
        duid: uid_t,
        dgid: gid_t,
        follow: bool,
    ) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), duid, dgid)
            } else {
                lchown(s.as_ptr(), duid, dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            self.wrap_chown(path, &meta, follow_arg)
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = self.wrap_chown(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chown<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_uid = self.dest_uid.unwrap_or(meta.uid());
        let dest_gid = self.dest_gid.unwrap_or(meta.gid());
        let path = path.as_ref();
        if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing ownership of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!(
                            "failed to change ownership of {} from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        );
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!(
                            "changed ownership of {} from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        );
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!(
                    "ownership of {} retained as {}:{}",
                    path.display(),
                    entries::uid2usr(dest_uid).unwrap(),
                    entries::gid2grp(dest_gid).unwrap()
                );
            }
        }
        ret
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}
<|endoftext|>


<commit_before>#[repr(i32)]
pub enum Format {
  Invalid = -1,
  ARGB32 = 0,
  RGB24 = 1,
  A8 = 2,
  A1 = 3,
  RGB16_565 = 4,
  RGB30 = 5
}

<commit_msg>Added documentation for Format.<commit_after>//! Identify memory format of image data.

///  cairo_format_t is used to identify the memory format of image data.
///
/// New entries may be added in future versions.
///
/// Since 1.0
#[repr(i32)]
pub enum Format {
  /// no such format exists or is supported.
  Invalid = -1,
  /// each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.) (Since 1.0)
  ARGB32 = 0,
  /// each pixel is a 32-bit quantity, with the upper 8 bits unused. Red, Green, and Blue are stored in the remaining 24 bits in that order. (Since 1.0)
  RGB24 = 1,
  /// each pixel is a 8-bit quantity holding an alpha value. (Since 1.0)
  A8 = 2,
  /// each pixel is a 1-bit quantity holding an alpha value. Pixels are packed together into 32-bit quantities. The ordering of the bits matches the endianess of the platform. On a big-endian machine, the first pixel is in the uppermost bit, on a little-endian machine the first pixel is in the least-significant bit. (Since 1.0)
  A1 = 3,
  /// each pixel is a 16-bit quantity with red in the upper 5 bits, then green in the middle 6 bits, and blue in the lower 5 bits. (Since 1.2)
  RGB16_565 = 4,
  /// like RGB24 but with 10bpc. (Since 1.12)
  RGB30 = 5
}

<|endoftext|>


<commit_before>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

use CharExt;
use Utf16Char;
extern crate std;
use std::fmt;

/// Iterate over the units in an UTF-16 representation of a codepoint.
#[derive(Clone,Copy)]
pub struct Utf16Iterator {
    first: Option<u16>,
    second: Option<u16>,
}
impl From<char> for Utf16Iterator {
    fn from(c: char) -> Self {
        let (first, second) = c.to_utf16_tuple();
        Utf16Iterator{ first: Some(first),  second: second }
    }
}
impl From<Utf16Char> for Utf16Iterator {
    fn from(uc: Utf16Char) -> Self {
        let (first, second) = uc.to_tuple();
        Utf16Iterator{ first: Some(first),  second: second }
    }
}
impl Iterator for Utf16Iterator {
    type Item=u16;
    fn next(&mut self) -> Option<u16> {
        match (self.first, self.second) {
            (Some(first), _)     => {self.first = None;   Some(first) },
            (None, Some(second)) => {self.second = None;  Some(second)},
            (None, None)         => {                     None        },
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len(), Some(self.len()))
    }
}
impl ExactSizeIterator for Utf16Iterator {
    fn len(&self) -> usize {
        match (self.first, self.second) {
            (None   ,  None   )  =>  0,
            (Some(_),  None   )  =>  1,
            (None   ,  Some(_))  =>  1,
            (Some(_),  Some(_))  =>  2,
        }
    }
}
impl fmt::Debug for Utf16Iterator {
    fn fmt(&self,  fmtr: &mut fmt::Formatter) -> fmt::Result {
        let v: Vec<u16> = self.collect();
        write!(fmtr, "{:?}", v)
    }
}
<commit_msg>Reduce size of Utf16Iterator<commit_after>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

use CharExt;
use Utf16Char;
extern crate std;
use std::fmt;

// Invalid values that says the field consumed or empty.
const FIRST_USED: u16 = 0x_dc_00;
const SECOND_USED: u16 = 0;

/// Iterate over the units in an UTF-16 representation of a codepoint.
#[derive(Clone,Copy)]
pub struct Utf16Iterator {
    first: u16,
    second: u16,
}
impl From<char> for Utf16Iterator {
    fn from(c: char) -> Self {
        let (first, second) = c.to_utf16_tuple();
        Utf16Iterator{ first: first,  second: second.unwrap_or(SECOND_USED) }
    }
}
impl From<Utf16Char> for Utf16Iterator {
    fn from(uc: Utf16Char) -> Self {
        let (first, second) = uc.to_tuple();
        Utf16Iterator{ first: first,  second: second.unwrap_or(SECOND_USED) }
    }
}
impl Iterator for Utf16Iterator {
    type Item=u16;
    fn next(&mut self) -> Option<u16> {
        match (self.first, self.second) {
            (FIRST_USED, SECOND_USED)  =>  {                            None        },
            (FIRST_USED, second     )  =>  {self.second = SECOND_USED;  Some(second)},
            (first     ,      _     )  =>  {self.first = FIRST_USED;    Some(first )},
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len(), Some(self.len()))
    }
}
impl ExactSizeIterator for Utf16Iterator {
    fn len(&self) -> usize {
        (if self.first == FIRST_USED {0} else {1}) +
        (if self.second == SECOND_USED {0} else {1})
    }
}
impl fmt::Debug for Utf16Iterator {
    fn fmt(&self,  fmtr: &mut fmt::Formatter) -> fmt::Result {
        let v: Vec<u16> = self.collect();
        write!(fmtr, "{:?}", v)
    }
}
<|endoftext|>