<commit_before>use std::env;
use std::io::{self, Write};
use std::process::{self, Command};

#[cfg(unix)]
use std::process::{Child, Stdio};

extern crate isatty;
use isatty::{stdout_isatty, stderr_isatty};

fn main() {
    let result = cargo_expand();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(&mut io::stderr(), "{}", err);
            1
        }
    });
}

#[cfg(windows)]
fn cargo_expand() -> io::Result<i32> {
    // Build cargo command
    let mut cmd = Command::new("cargo");
    cmd.args(&wrap_args(env::args()));
    run(cmd)
}

#[cfg(unix)]
fn cargo_expand() -> io::Result<i32> {
    if env::args().last().unwrap() == "--filter-rustfmt" {
        filter_rustfmt();
    }

    // Build cargo command
    let mut cmd = Command::new("cargo");
    cmd.args(&wrap_args(env::args()));

    // Pipe to rustfmt
    let _wait = match which(&["rustfmt"]) {
        Some(ref fmt) => {
            let args: Vec<_> = env::args().collect();
            let mut filter_args = Vec::new();
            for i in 0..args.len() {
                filter_args.push(args[i].as_str());
            }
            filter_args.push("--filter-rustfmt");

            Some((
                // Work around $crate issue https://github.com/rust-lang/rust/issues/38016
                try!(cmd.pipe_to(&["sed", "s/$crate/XCRATE/g"], None)),
                try!(cmd.pipe_to(&[fmt], None)),
                try!(cmd.pipe_to(&["sed", "s/XCRATE/$crate/g"], Some(&filter_args))),
            ))
        }
        None => None,
    };

    // Pipe to pygmentize
    let _wait = match which(&["pygmentize", "-l", "rust"]) {
        Some(pyg) => Some(try!(cmd.pipe_to(&[&pyg, "-l", "rust"], None))),
        None => None,
    };

    run(cmd)
}

fn run(mut cmd: Command) -> io::Result<i32> {
    cmd.status().map(|status| status.code().unwrap_or(1))
}

#[cfg(unix)]
struct Wait(Vec<Child>);

#[cfg(unix)]
impl Drop for Wait {
    fn drop(&mut self) {
        for child in &mut self.0 {
            if let Err(err) = child.wait() {
                let _ = writeln!(&mut io::stderr(), "{}", err);
            }
        }
    }
}

#[cfg(unix)]
trait PipeTo {
    fn pipe_to(&mut self, out: &[&str], err: Option<&[&str]>) -> io::Result<Wait>;
}

#[cfg(unix)]
impl PipeTo for Command {
    fn pipe_to(&mut self, out: &[&str], err: Option<&[&str]>) -> io::Result<Wait> {
        use std::os::unix::io::{AsRawFd, FromRawFd};

        self.stdout(Stdio::piped());
        if err.is_some() {
            self.stderr(Stdio::piped());
        }

        let child = try!(self.spawn());

        *self = Command::new(out[0]);
        self.args(&out[1..]);
        self.stdin(unsafe {
            Stdio::from_raw_fd(child.stdout.as_ref().map(AsRawFd::as_raw_fd).unwrap())
        });

        match err {
            None => {
                Ok(Wait(vec![child]))
            }
            Some(err) => {
                let mut errcmd = Command::new(err[0]);
                errcmd.args(&err[1..]);
                errcmd.stdin(unsafe {
                    Stdio::from_raw_fd(child.stderr.as_ref().map(AsRawFd::as_raw_fd).unwrap())
                });
                errcmd.stdout(Stdio::null());
                errcmd.stderr(Stdio::inherit());
                let spawn = try!(errcmd.spawn());
                Ok(Wait(vec![spawn, child]))
            }
        }
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn wrap_args<T, I>(it: I) -> Vec<String>
    where T: AsRef<str>,
          I: IntoIterator<Item=T>
{
    let mut args = vec!["rustc".to_string()];
    let mut has_color = false;
    let mut has_double_hyphen = false;

    for arg in it.into_iter().skip(2) {
        let arg = arg.as_ref().to_string();
        has_color |= arg.starts_with("--color");
        has_double_hyphen |= arg == "--";
        args.push(arg);
    }

    if !has_color {
        let color = stdout_isatty() && stderr_isatty();
        let setting = if color { "always" } else { "never" };
        args.push(format!("--color={}", setting));
    }

    if !has_double_hyphen {
        args.push("--".to_string());
    }

    args.push("-Zunstable-options".to_string());
    args.push("--pretty=expanded".to_string());

    args
}

#[cfg(unix)]
fn which(cmd: &[&str]) -> Option<String> {
    if env::args().find(|arg| arg == "--help").is_some() {
        None
    } else {
        match env::var(&cmd[0].to_uppercase()) {
            Ok(which) => {
                if which.is_empty() {
                    None
                } else {
                    Some(which)
                }
            }
            Err(_) => {
                let in_path = Command::new(cmd[0])
                    .args(&cmd[1..])
                    .stdin(Stdio::null())
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .spawn()
                    .is_ok();
                if in_path {
                    Some(cmd[0].to_string())
                } else {
                    None
                }
            }
        }
    }
}

#[cfg(unix)]
fn filter_rustfmt() -> ! {
    let mut line = String::new();
    while let Ok(n) = io::stdin().read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore_rustfmt_err(&line) {
            let _ = write!(&mut io::stderr(), "{}", line);
        }
        line.clear();
    }
    process::exit(0);
}

#[cfg(unix)]
fn ignore_rustfmt_err(line: &str) -> bool {
    line.trim().is_empty()
        || line.trim_right().ends_with("line exceeded maximum length (sorry)")
        || line.trim_right().ends_with("left behind trailing whitespace (sorry)")
}
<commit_msg>Expand the test.rs test by default<commit_after>use std::env;
use std::io::{self, Write};
use std::process::{self, Command};

#[cfg(unix)]
use std::process::{Child, Stdio};

extern crate isatty;
use isatty::{stdout_isatty, stderr_isatty};

fn main() {
    let result = cargo_expand();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(&mut io::stderr(), "{}", err);
            1
        }
    });
}

#[cfg(windows)]
fn cargo_expand() -> io::Result<i32> {
    // Build cargo command
    let mut cmd = Command::new("cargo");
    cmd.args(&wrap_args(env::args()));
    run(cmd)
}

#[cfg(unix)]
fn cargo_expand() -> io::Result<i32> {
    if env::args().last().unwrap() == "--filter-rustfmt" {
        filter_rustfmt();
    }

    // Build cargo command
    let mut cmd = Command::new("cargo");
    cmd.args(&wrap_args(env::args()));

    // Pipe to rustfmt
    let _wait = match which(&["rustfmt"]) {
        Some(ref fmt) => {
            let args: Vec<_> = env::args().collect();
            let mut filter_args = Vec::new();
            for i in 0..args.len() {
                filter_args.push(args[i].as_str());
            }
            filter_args.push("--filter-rustfmt");

            Some((
                // Work around $crate issue https://github.com/rust-lang/rust/issues/38016
                try!(cmd.pipe_to(&["sed", "s/$crate/XCRATE/g"], None)),
                try!(cmd.pipe_to(&[fmt], None)),
                try!(cmd.pipe_to(&["sed", "s/XCRATE/$crate/g"], Some(&filter_args))),
            ))
        }
        None => None,
    };

    // Pipe to pygmentize
    let _wait = match which(&["pygmentize", "-l", "rust"]) {
        Some(pyg) => Some(try!(cmd.pipe_to(&[&pyg, "-l", "rust"], None))),
        None => None,
    };

    run(cmd)
}

fn run(mut cmd: Command) -> io::Result<i32> {
    cmd.status().map(|status| status.code().unwrap_or(1))
}

#[cfg(unix)]
struct Wait(Vec<Child>);

#[cfg(unix)]
impl Drop for Wait {
    fn drop(&mut self) {
        for child in &mut self.0 {
            if let Err(err) = child.wait() {
                let _ = writeln!(&mut io::stderr(), "{}", err);
            }
        }
    }
}

#[cfg(unix)]
trait PipeTo {
    fn pipe_to(&mut self, out: &[&str], err: Option<&[&str]>) -> io::Result<Wait>;
}

#[cfg(unix)]
impl PipeTo for Command {
    fn pipe_to(&mut self, out: &[&str], err: Option<&[&str]>) -> io::Result<Wait> {
        use std::os::unix::io::{AsRawFd, FromRawFd};

        self.stdout(Stdio::piped());
        if err.is_some() {
            self.stderr(Stdio::piped());
        }

        let child = try!(self.spawn());

        *self = Command::new(out[0]);
        self.args(&out[1..]);
        self.stdin(unsafe {
            Stdio::from_raw_fd(child.stdout.as_ref().map(AsRawFd::as_raw_fd).unwrap())
        });

        match err {
            None => {
                Ok(Wait(vec![child]))
            }
            Some(err) => {
                let mut errcmd = Command::new(err[0]);
                errcmd.args(&err[1..]);
                errcmd.stdin(unsafe {
                    Stdio::from_raw_fd(child.stderr.as_ref().map(AsRawFd::as_raw_fd).unwrap())
                });
                errcmd.stdout(Stdio::null());
                errcmd.stderr(Stdio::inherit());
                let spawn = try!(errcmd.spawn());
                Ok(Wait(vec![spawn, child]))
            }
        }
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn wrap_args<T, I>(it: I) -> Vec<String>
    where T: AsRef<str>,
          I: IntoIterator<Item=T>
{
    let mut args = vec!["rustc".to_string()];
    let mut has_color = false;
    let mut has_double_hyphen = false;
    let mut ends_with_test = false;

    for arg in it.into_iter().skip(2) {
        let arg = arg.as_ref().to_string();
        has_color |= arg.starts_with("--color");
        has_double_hyphen |= arg == "--";
        ends_with_test = arg == "--test";
        args.push(arg);
    }

    if !has_double_hyphen && ends_with_test {
        // Expand the `test.rs` test by default.
        args.push("test".to_string());
    }

    if !has_color {
        let color = stdout_isatty() && stderr_isatty();
        let setting = if color { "always" } else { "never" };
        args.push(format!("--color={}", setting));
    }

    if !has_double_hyphen {
        args.push("--".to_string());
    }

    args.push("-Zunstable-options".to_string());
    args.push("--pretty=expanded".to_string());

    args
}

#[cfg(unix)]
fn which(cmd: &[&str]) -> Option<String> {
    if env::args().find(|arg| arg == "--help").is_some() {
        None
    } else {
        match env::var(&cmd[0].to_uppercase()) {
            Ok(which) => {
                if which.is_empty() {
                    None
                } else {
                    Some(which)
                }
            }
            Err(_) => {
                let in_path = Command::new(cmd[0])
                    .args(&cmd[1..])
                    .stdin(Stdio::null())
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .spawn()
                    .is_ok();
                if in_path {
                    Some(cmd[0].to_string())
                } else {
                    None
                }
            }
        }
    }
}

#[cfg(unix)]
fn filter_rustfmt() -> ! {
    let mut line = String::new();
    while let Ok(n) = io::stdin().read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore_rustfmt_err(&line) {
            let _ = write!(&mut io::stderr(), "{}", line);
        }
        line.clear();
    }
    process::exit(0);
}

#[cfg(unix)]
fn ignore_rustfmt_err(line: &str) -> bool {
    line.trim().is_empty()
        || line.trim_right().ends_with("line exceeded maximum length (sorry)")
        || line.trim_right().ends_with("left behind trailing whitespace (sorry)")
}
<|endoftext|>


<commit_before>extern crate cargo_update;
extern crate tabwriter;
extern crate lazysort;
extern crate regex;
extern crate git2;

use std::process::{Command, exit};
use std::io::{Write, stdout};
use std::fs::{self, File};
use tabwriter::TabWriter;
use lazysort::SortedBy;
use std::fmt::Display;
use git2::Repository;
use regex::Regex;
use std::env;

fn main() {
    let result = actual_main().err().unwrap_or(0);
    exit(result);
}

fn actual_main() -> Result<(), i32> {
    let opts = cargo_update::Options::parse();

    if cfg!(target_os = "windows") {
        let old_version_r = Regex::new(r"cargo-install-update\.exe-v.+").unwrap();
        for old_version in fs::read_dir(env::current_exe().unwrap().parent().unwrap().canonicalize().unwrap())
            .unwrap()
            .map(Result::unwrap)
            .filter(|f| old_version_r.is_match(&f.file_name().into_string().unwrap())) {
            fs::remove_file(old_version.path()).unwrap();
        }
    }

    let crates_file = cargo_update::ops::resolve_crates_file(opts.crates_file.1.clone());
    let configuration = try!(cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml")));
    let mut packages = cargo_update::ops::installed_main_repo_packages(&crates_file);

    if !opts.to_update.is_empty() {
        packages = cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install);
    }

    {
        // Searching for "" will just update the registry
        let search_res = Command::new("cargo").arg("search").arg("").status().unwrap();
        if !search_res.success() {
            return Err(search_res.code().unwrap_or(-1));
        }
        println!("");
    }

    let registry = cargo_update::ops::get_index_path(&opts.cargo_dir.1);
    let registry_repo = try!(Repository::open(&registry).map_err(|_| {
        println!("Failed to open registry repository at {}.", registry.display());
        2
    }));
    let latest_registry = try!(registry_repo.revparse_single("origin/master").map_err(|_| {
        println!("Failed read master branch of registry repositry at {}.", registry.display());
        2
    }));

    for package in &mut packages {
        package.pull_version(&latest_registry.as_commit().unwrap().tree().unwrap(), &registry_repo);
    }

    {
        let mut out = TabWriter::new(stdout());
        writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
        for package in packages.iter().sorted_by(|lhs, rhs| (!lhs.needs_update()).cmp(&!rhs.needs_update())) {
            write!(out, "{}\t", package.name).unwrap();
            if let Some(ref v) = package.version {
                write!(out, "v{}", v).unwrap();
            }
            writeln!(out, "\tv{}\t{}", package.update_to_version(), if package.needs_update() { "Yes" } else { "No" }).unwrap();
        }
        writeln!(out, "").unwrap();
        out.flush().unwrap();
    }

    if opts.update {
        if !opts.force {
            packages.retain(cargo_update::ops::MainRepoPackage::needs_update);
        }

        if !packages.is_empty() {
            let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                .map(|package| -> Result<(), (i32, String)> {
                    println!("{} {}",
                             if package.version.is_some() {
                                 "Updating"
                             } else {
                                 "Installing"
                             },
                             package.name);

                    if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                        save_cargo_update_exec(package.version.as_ref().unwrap());
                    }

                    let install_res = if let Some(cfg) = configuration.get(&package.name) {
                            Command::new("cargo")
                                .args(&cfg.cargo_args()[..])
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().to_string())
                                .status()
                        } else {
                            Command::new("cargo")
                                .arg("install")
                                .arg("-f")
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().to_string())
                                .status()
                        }
                        .unwrap();

                    println!("");
                    if !install_res.success() {
                        if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                            restore_cargo_update_exec(package.version.as_ref().unwrap());
                        }

                        Err((install_res.code().unwrap_or(-1), package.name))
                    } else {
                        Ok(())
                    }
                })
                .fold((0, vec![], None), |(s, mut e, r), p| match p {
                    Ok(()) => (s + 1, e, r),
                    Err((pr, pn)) => {
                        e.push(pn);
                        (s, e, r.or_else(|| Some(pr)))
                    }
                });

            println!("");
            println!("Updated {} package{}.", success_n, if success_n == 1 { "" } else { "s" });
            if !errored.is_empty() && result.is_some() {
                println!("Failed to update {}.", &errored.iter().fold("".to_string(), |s, e| s + ", " + e)[2..]);
                return Err(result.unwrap());
            }
        } else {
            println!("No packages need updating.");
        }
    }

    if opts.update_git {
        let mut packages = cargo_update::ops::installed_git_repo_packages(&crates_file);

        if !opts.to_update.is_empty() {
            packages.retain(|p| opts.to_update.iter().any(|u| p.name == u.0));
        }

        for package in &mut packages {
            package.pull_version(&opts.temp_dir.1);
        }

        {
            let mut out = TabWriter::new(stdout());
            writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
            for package in packages.iter().sorted_by(|lhs, rhs| (!lhs.needs_update()).cmp(&!rhs.needs_update())) {
                writeln!(out,
                         "{}\t{}\t{}\t{}",
                         package.name,
                         package.id,
                         package.newest_id.as_ref().unwrap(),
                         if package.needs_update() { "Yes" } else { "No" })
                    .unwrap();
            }
            writeln!(out, "").unwrap();
            out.flush().unwrap();
        }

        if opts.update {
            if !opts.force {
                packages.retain(cargo_update::ops::GitRepoPackage::needs_update);
            }

            if !packages.is_empty() {
                let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                    .map(|package| -> Result<(), (i32, String)> {
                        println!("Installing {} from {}", package.name, package.url);

                        if cfg!(target_os = "windows") && package.name == "cargo-update" {
                            save_cargo_update_exec(&package.id.to_string());
                        }

                        let install_res = if let Some(cfg) = configuration.get(&package.name) {
                                Command::new("cargo")
                                    .args(&cfg.cargo_args()[..])
                                    .arg("--git")
                                    .arg(&package.url)
                                    .status()
                            } else {
                                Command::new("cargo")
                                    .arg("install")
                                    .arg("-f")
                                    .arg("--git")
                                    .arg(&package.url)
                                    .status()
                            }
                            .unwrap();

                        println!("");
                        if !install_res.success() {
                            if cfg!(target_os = "windows") && package.name == "cargo-update" {
                                restore_cargo_update_exec(&package.id.to_string());
                            }

                            Err((install_res.code().unwrap_or(-1), package.name))
                        } else {
                            Ok(())
                        }
                    })
                    .fold((0, vec![], None), |(s, mut e, r), p| match p {
                        Ok(()) => (s + 1, e, r),
                        Err((pr, pn)) => {
                            e.push(pn);
                            (s, e, r.or_else(|| Some(pr)))
                        }
                    });

                println!("");
                println!("Updated {} git package{}.", success_n, if success_n == 1 { "" } else { "s" });
                if !errored.is_empty() && result.is_some() {
                    println!("Failed to update {}.", &errored.iter().fold("".to_string(), |s, e| s + ", " + e)[2..]);
                    return Err(result.unwrap());
                }
            } else {
                println!("No git packages need updating.");
            }
        }
    }

    Ok(())
}

/// This way the past-current exec will be "replaced" and we'll get no dupes in .cargo.toml
#[cfg(target_os="windows")]
fn save_cargo_update_exec<D: Display>(version: &D) {
    let cur_exe = env::current_exe().unwrap();
    fs::rename(&cur_exe, cur_exe.with_extension(format!("exe-v{}", version))).unwrap();
    File::create(cur_exe).unwrap();
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec<D: Display>(version: &D) {
    let cur_exe = env::current_exe().unwrap();
    fs::remove_file(&cur_exe).unwrap();
    fs::rename(cur_exe.with_extension(format!("exe-v{}", version)), cur_exe).unwrap();
}

#[cfg(not(target_os="windows"))]
fn save_cargo_update_exec<D: Display>(_: &D) {}

#[cfg(not(target_os="windows"))]
fn restore_cargo_update_exec<D: Display>(_: &D) {}
<commit_msg>Fix clippy<commit_after>extern crate cargo_update;
extern crate tabwriter;
extern crate lazysort;
extern crate regex;
extern crate git2;

use std::process::{Command, exit};
use std::io::{Write, stdout};
use tabwriter::TabWriter;
use lazysort::SortedBy;
use std::fmt::Display;
use git2::Repository;
#[cfg(target_os="windows")]
use std::fs::File;
use regex::Regex;
use std::env;
use std::fs;

fn main() {
    let result = actual_main().err().unwrap_or(0);
    exit(result);
}

fn actual_main() -> Result<(), i32> {
    let opts = cargo_update::Options::parse();

    if cfg!(target_os = "windows") {
        let old_version_r = Regex::new(r"cargo-install-update\.exe-v.+").unwrap();
        for old_version in fs::read_dir(env::current_exe().unwrap().parent().unwrap().canonicalize().unwrap())
            .unwrap()
            .map(Result::unwrap)
            .filter(|f| old_version_r.is_match(&f.file_name().into_string().unwrap())) {
            fs::remove_file(old_version.path()).unwrap();
        }
    }

    let crates_file = cargo_update::ops::resolve_crates_file(opts.crates_file.1.clone());
    let configuration = try!(cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml")));
    let mut packages = cargo_update::ops::installed_main_repo_packages(&crates_file);

    if !opts.to_update.is_empty() {
        packages = cargo_update::ops::intersect_packages(&packages, &opts.to_update, opts.install);
    }

    {
        // Searching for "" will just update the registry
        let search_res = Command::new("cargo").arg("search").arg("").status().unwrap();
        if !search_res.success() {
            return Err(search_res.code().unwrap_or(-1));
        }
        println!("");
    }

    let registry = cargo_update::ops::get_index_path(&opts.cargo_dir.1);
    let registry_repo = try!(Repository::open(&registry).map_err(|_| {
        println!("Failed to open registry repository at {}.", registry.display());
        2
    }));
    let latest_registry = try!(registry_repo.revparse_single("origin/master").map_err(|_| {
        println!("Failed read master branch of registry repositry at {}.", registry.display());
        2
    }));

    for package in &mut packages {
        package.pull_version(&latest_registry.as_commit().unwrap().tree().unwrap(), &registry_repo);
    }

    {
        let mut out = TabWriter::new(stdout());
        writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
        for package in packages.iter().sorted_by(|lhs, rhs| (!lhs.needs_update()).cmp(&!rhs.needs_update())) {
            write!(out, "{}\t", package.name).unwrap();
            if let Some(ref v) = package.version {
                write!(out, "v{}", v).unwrap();
            }
            writeln!(out, "\tv{}\t{}", package.update_to_version(), if package.needs_update() { "Yes" } else { "No" }).unwrap();
        }
        writeln!(out, "").unwrap();
        out.flush().unwrap();
    }

    if opts.update {
        if !opts.force {
            packages.retain(cargo_update::ops::MainRepoPackage::needs_update);
        }

        if !packages.is_empty() {
            let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                .map(|package| -> Result<(), (i32, String)> {
                    println!("{} {}",
                             if package.version.is_some() {
                                 "Updating"
                             } else {
                                 "Installing"
                             },
                             package.name);

                    if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                        save_cargo_update_exec(package.version.as_ref().unwrap());
                    }

                    let install_res = if let Some(cfg) = configuration.get(&package.name) {
                            Command::new("cargo")
                                .args(&cfg.cargo_args()[..])
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().to_string())
                                .status()
                        } else {
                            Command::new("cargo")
                                .arg("install")
                                .arg("-f")
                                .arg(&package.name)
                                .arg("--vers")
                                .arg(package.update_to_version().to_string())
                                .status()
                        }
                        .unwrap();

                    println!("");
                    if !install_res.success() {
                        if cfg!(target_os = "windows") && package.version.is_some() && package.name == "cargo-update" {
                            restore_cargo_update_exec(package.version.as_ref().unwrap());
                        }

                        Err((install_res.code().unwrap_or(-1), package.name))
                    } else {
                        Ok(())
                    }
                })
                .fold((0, vec![], None), |(s, mut e, r), p| match p {
                    Ok(()) => (s + 1, e, r),
                    Err((pr, pn)) => {
                        e.push(pn);
                        (s, e, r.or_else(|| Some(pr)))
                    }
                });

            println!("");
            println!("Updated {} package{}.", success_n, if success_n == 1 { "" } else { "s" });
            if !errored.is_empty() && result.is_some() {
                println!("Failed to update {}.", &errored.iter().fold("".to_string(), |s, e| s + ", " + e)[2..]);
                return Err(result.unwrap());
            }
        } else {
            println!("No packages need updating.");
        }
    }

    if opts.update_git {
        let mut packages = cargo_update::ops::installed_git_repo_packages(&crates_file);

        if !opts.to_update.is_empty() {
            packages.retain(|p| opts.to_update.iter().any(|u| p.name == u.0));
        }

        for package in &mut packages {
            package.pull_version(&opts.temp_dir.1);
        }

        {
            let mut out = TabWriter::new(stdout());
            writeln!(out, "Package\tInstalled\tLatest\tNeeds update").unwrap();
            for package in packages.iter().sorted_by(|lhs, rhs| (!lhs.needs_update()).cmp(&!rhs.needs_update())) {
                writeln!(out,
                         "{}\t{}\t{}\t{}",
                         package.name,
                         package.id,
                         package.newest_id.as_ref().unwrap(),
                         if package.needs_update() { "Yes" } else { "No" })
                    .unwrap();
            }
            writeln!(out, "").unwrap();
            out.flush().unwrap();
        }

        if opts.update {
            if !opts.force {
                packages.retain(cargo_update::ops::GitRepoPackage::needs_update);
            }

            if !packages.is_empty() {
                let (success_n, errored, result): (usize, Vec<String>, Option<i32>) = packages.into_iter()
                    .map(|package| -> Result<(), (i32, String)> {
                        println!("Installing {} from {}", package.name, package.url);

                        if cfg!(target_os = "windows") && package.name == "cargo-update" {
                            save_cargo_update_exec(&package.id.to_string());
                        }

                        let install_res = if let Some(cfg) = configuration.get(&package.name) {
                                Command::new("cargo")
                                    .args(&cfg.cargo_args()[..])
                                    .arg("--git")
                                    .arg(&package.url)
                                    .status()
                            } else {
                                Command::new("cargo")
                                    .arg("install")
                                    .arg("-f")
                                    .arg("--git")
                                    .arg(&package.url)
                                    .status()
                            }
                            .unwrap();

                        println!("");
                        if !install_res.success() {
                            if cfg!(target_os = "windows") && package.name == "cargo-update" {
                                restore_cargo_update_exec(&package.id.to_string());
                            }

                            Err((install_res.code().unwrap_or(-1), package.name))
                        } else {
                            Ok(())
                        }
                    })
                    .fold((0, vec![], None), |(s, mut e, r), p| match p {
                        Ok(()) => (s + 1, e, r),
                        Err((pr, pn)) => {
                            e.push(pn);
                            (s, e, r.or_else(|| Some(pr)))
                        }
                    });

                println!("");
                println!("Updated {} git package{}.", success_n, if success_n == 1 { "" } else { "s" });
                if !errored.is_empty() && result.is_some() {
                    println!("Failed to update {}.", &errored.iter().fold("".to_string(), |s, e| s + ", " + e)[2..]);
                    return Err(result.unwrap());
                }
            } else {
                println!("No git packages need updating.");
            }
        }
    }

    Ok(())
}

/// This way the past-current exec will be "replaced" and we'll get no dupes in .cargo.toml
#[cfg(target_os="windows")]
fn save_cargo_update_exec<D: Display>(version: &D) {
    let cur_exe = env::current_exe().unwrap();
    fs::rename(&cur_exe, cur_exe.with_extension(format!("exe-v{}", version))).unwrap();
    File::create(cur_exe).unwrap();
}

#[cfg(target_os="windows")]
fn restore_cargo_update_exec<D: Display>(version: &D) {
    let cur_exe = env::current_exe().unwrap();
    fs::remove_file(&cur_exe).unwrap();
    fs::rename(cur_exe.with_extension(format!("exe-v{}", version)), cur_exe).unwrap();
}

#[cfg(not(target_os="windows"))]
fn save_cargo_update_exec<D: Display>(_: &D) {}

#[cfg(not(target_os="windows"))]
fn restore_cargo_update_exec<D: Display>(_: &D) {}
<|endoftext|>


<commit_before>extern crate oauth_client as oath;
extern crate rand;
extern crate twitter_api as twitter;

pub mod badvestments;
pub mod ast;

use oath::Token;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::collections::HashMap;
use rand::Rng;

struct SubstitutionMap {
    map: HashMap<String, Vec<Vec<String>>>
}

impl SubstitutionMap {
    fn new() -> SubstitutionMap {
        SubstitutionMap { map: HashMap::new() }
    }

    fn add_substitution(&mut self, substitution : ast::Substitution) {
        let mut insert_as_new = false;
        match self.map.get_mut(&substitution.s) {
            Some(ref mut substitutions) => {
                substitutions.push(substitution.v.clone());
            },
            None => {
                insert_as_new = true;
            }
        }
        if insert_as_new {
            self.map.insert(substitution.s, vec![substitution.v]);
        }
    }

    fn apply_substitutions(&self, symbols : Vec<String>) -> Vec<String> {
        let mut results: Vec<String> = vec![];
        for symbol in symbols {
            match self.map.get(&symbol) {
                Some(substitutions) => {
                    let chosen = rand::thread_rng().choose(&substitutions).unwrap();
                    for s in chosen.iter() {
                        results.push(s.clone());
                    }
                },
                None => {
                    results.push(symbol);
                }
            }
        }
        return results;
    }
}

fn prompt(msg: &str) -> String {
    print!("{}: ", msg);
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    return line.trim().to_string();
}

struct AccessToken {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String,
}

impl AccessToken {
    pub fn read() -> Option<AccessToken> {
        let mut token = AccessToken { consumer_key: String::new(), consumer_secret: String::new(),
                                      access_key: String::new(), access_secret: String::new() };
        let file = match File::open("badvestments.keys") {
            Ok(f) => f,
            Err(_) => return None,
        };
        let reader = BufReader::new(&file);
        let mut lines = reader.lines();
        token.consumer_key = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.consumer_secret = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.access_key = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.access_secret = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        return Some(token);
    }
}

fn main() {
    let file = File::open("badvestments.rules").unwrap();
    let reader = BufReader::new(&file);
    let mut substitutions = SubstitutionMap::new();
    for line in reader.lines() {
        let l = line.unwrap();
        let result = badvestments::parse_Rule(l.as_str());
        match result {
            Ok(substitution) => {
                substitutions.add_substitution(substitution);
            },
            _ => {
                println!("(error parsing)");
            }
        }
    };
    let mut badvestment = vec![String::from("Badvestment")];
    let mut next = substitutions.apply_substitutions(badvestment.clone());
    while badvestment != next {
        badvestment = next;
        next = substitutions.apply_substitutions(badvestment.clone());
    }
    let results: Vec<String> = badvestment.into_iter()
                                          .map(|s| { s.replace('"', "") })
                                          .collect();

    let tweet = results.join(" ");
    println!("{}", tweet);
    let token = match AccessToken::read() {
        Some(t) => t,
        None => {
            println!("log in to twitter and visit https://apps.twitter.com/app");
            println!("go to the 'Keys and Access Tokens' tab and click");
            println!("'(Re)generate My Access Token and Token Secret'");
            println!("Copy your Consumer Key, Consumer Secret, Access Token, and");
            println!("Access Token Secret and save them in badvestments.keys");
            panic!("couldn't read access tokens");
        },
    };
    let consumer = Token::new(token.consumer_key, token.consumer_secret);
    let access = Token::new(token.access_key, token.access_secret);

    let doit = prompt("tweet? Y/N");
    match doit.as_ref() {
        "Y" => {
            twitter::update_status(&consumer, &access, &tweet).unwrap();
        },
        _ => {
            println!("ok - didn't tweet that.");
        }
    }
}
<commit_msg>lowercase is the way to go here<commit_after>extern crate oauth_client as oath;
extern crate rand;
extern crate twitter_api as twitter;

pub mod badvestments;
pub mod ast;

use oath::Token;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::collections::HashMap;
use rand::Rng;

struct SubstitutionMap {
    map: HashMap<String, Vec<Vec<String>>>
}

impl SubstitutionMap {
    fn new() -> SubstitutionMap {
        SubstitutionMap { map: HashMap::new() }
    }

    fn add_substitution(&mut self, substitution : ast::Substitution) {
        let mut insert_as_new = false;
        match self.map.get_mut(&substitution.s) {
            Some(ref mut substitutions) => {
                substitutions.push(substitution.v.clone());
            },
            None => {
                insert_as_new = true;
            }
        }
        if insert_as_new {
            self.map.insert(substitution.s, vec![substitution.v]);
        }
    }

    fn apply_substitutions(&self, symbols : Vec<String>) -> Vec<String> {
        let mut results: Vec<String> = vec![];
        for symbol in symbols {
            match self.map.get(&symbol) {
                Some(substitutions) => {
                    let chosen = rand::thread_rng().choose(&substitutions).unwrap();
                    for s in chosen.iter() {
                        results.push(s.clone());
                    }
                },
                None => {
                    results.push(symbol);
                }
            }
        }
        return results;
    }
}

fn prompt(msg: &str) -> String {
    print!("{}: ", msg);
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    return line.trim().to_string();
}

struct AccessToken {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String,
}

impl AccessToken {
    pub fn read() -> Option<AccessToken> {
        let mut token = AccessToken { consumer_key: String::new(), consumer_secret: String::new(),
                                      access_key: String::new(), access_secret: String::new() };
        let file = match File::open("badvestments.keys") {
            Ok(f) => f,
            Err(_) => return None,
        };
        let reader = BufReader::new(&file);
        let mut lines = reader.lines();
        token.consumer_key = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.consumer_secret = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.access_key = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        token.access_secret = match lines.next() {
            Some(result) => match result {
                Ok(val) => val,
                Err(_) => return None,
            },
            None => return None,
        };
        return Some(token);
    }
}

fn main() {
    let file = File::open("badvestments.rules").unwrap();
    let reader = BufReader::new(&file);
    let mut substitutions = SubstitutionMap::new();
    for line in reader.lines() {
        let l = line.unwrap();
        let result = badvestments::parse_Rule(l.as_str());
        match result {
            Ok(substitution) => {
                substitutions.add_substitution(substitution);
            },
            _ => {
                println!("(error parsing)");
            }
        }
    };
    let mut badvestment = vec![String::from("Badvestment")];
    let mut next = substitutions.apply_substitutions(badvestment.clone());
    while badvestment != next {
        badvestment = next;
        next = substitutions.apply_substitutions(badvestment.clone());
    }
    let results: Vec<String> = badvestment.into_iter()
                                          .map(|s| { s.replace('"', "") })
                                          .collect();

    let tweet = results.join(" ");
    println!("{}", tweet);
    let token = match AccessToken::read() {
        Some(t) => t,
        None => {
            println!("log in to twitter and visit https://apps.twitter.com/app");
            println!("go to the 'Keys and Access Tokens' tab and click");
            println!("'(Re)generate My Access Token and Token Secret'");
            println!("Copy your Consumer Key, Consumer Secret, Access Token, and");
            println!("Access Token Secret and save them in badvestments.keys");
            panic!("couldn't read access tokens");
        },
    };
    let consumer = Token::new(token.consumer_key, token.consumer_secret);
    let access = Token::new(token.access_key, token.access_secret);

    let doit = prompt("tweet? y/n");
    match doit.as_ref() {
        "y" => {
            twitter::update_status(&consumer, &access, &tweet).unwrap();
        },
        _ => {
            println!("ok - didn't tweet that.");
        }
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate "rust-nc" as rustnc;

use getopts::{Options,Matches};
use std::os;

use rustnc::{print_usage,nc};

fn main() {
    let args: Vec<String> = os::args();

    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("l", "listen", "Listen for incoming connections.");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(args.tail()) {
        Ok(m) => { m }
        Err(f) => {
            print_usage(program.as_slice(), opts);
            panic!(f.to_string())
        }
    };
    if matches.opt_present("h") {
        print_usage(program.as_slice(), opts);
        return;
    }

    nc(&matches);
}
<commit_msg>Removing unused getopts::Matches in main.rs<commit_after>extern crate getopts;
extern crate "rust-nc" as rustnc;

use getopts::{Options};
use std::os;

use rustnc::{print_usage,nc};

fn main() {
    let args: Vec<String> = os::args();

    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("l", "listen", "Listen for incoming connections.");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(args.tail()) {
        Ok(m) => { m }
        Err(f) => {
            print_usage(program.as_slice(), opts);
            panic!(f.to_string())
        }
    };
    if matches.opt_present("h") {
        print_usage(program.as_slice(), opts);
        return;
    }

    nc(&matches);
}
<|endoftext|>


<commit_before>mod token;
mod lexer;

fn main() {
    println!("Hello, world!");
}
<commit_msg>Removed well-known phrase (broken project for now).<commit_after>mod token;
mod lexer;

fn main() {}
<|endoftext|>


<commit_before>#![feature(ip_addr, convert, std_misc)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::mem::transmute;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
use std::time::duration::Duration;

#[macro_use]
mod testing;

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut buf = [0u8; 256];
  loop {
    match socket.recv_from(&mut buf) {
      Ok((len, src_addr)) => {
        if len == 1 && buf[0] == 1 {
          // ping!
          println!("ping!!!");
          unsafe {
            let response = [1u8];
            socket.send_to(response.as_ref(), src_addr).unwrap();
          }
        }
      }

      Err(e) => println!("Failed to receive :(! error: {}", e)
    }
    println!("going again!");
  }
}

fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

// NOTE This MUST be run before any server tests!
#[test]
fn start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}

#[test]
fn server_can_receive_packet() {
  server_test!((socket, host_addr) {
    let mut buf = [0u8; 256];
    buf[0] = 90;
    unsafe { socket.send_to(buf.as_ref(), host_addr).unwrap(); };
  });
}

#[test]
fn server_can_be_pinged() {
  server_test!((socket, host_addr) {
    unsafe {
      let mut buf = [1u8];
      socket.send_to(buf.as_ref(), host_addr).unwrap();

      let mut in_buf = [0u8, 256];
      let ms = Duration::span(|| {
        match socket.recv_from(&mut in_buf) {
          Ok((len, _src_addr)) => {
            // I think this keeps ending up 2
            assert_eq!(len, 1);
            assert_eq!(in_buf[0], 1);
          }

          Err(e) => panic!("Got error {} when trying to ping!", e)
        }
      });
      println!("Ping took {} ms!", ms.num_milliseconds());
    }
  });
}
<commit_msg>remove useless comment<commit_after>#![feature(ip_addr, convert, std_misc)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::mem::transmute;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
use std::time::duration::Duration;

#[macro_use]
mod testing;

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut buf = [0u8; 256];
  loop {
    match socket.recv_from(&mut buf) {
      Ok((len, src_addr)) => {
        if len == 1 && buf[0] == 1 {
          // ping!
          println!("ping!!!");
          unsafe {
            let response = [1u8];
            socket.send_to(response.as_ref(), src_addr).unwrap();
          }
        }
      }

      Err(e) => println!("Failed to receive :(! error: {}", e)
    }
    println!("going again!");
  }
}

fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

// NOTE This MUST be run before any server tests!
#[test]
fn start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}

#[test]
fn server_can_receive_packet() {
  server_test!((socket, host_addr) {
    let mut buf = [0u8; 256];
    buf[0] = 90;
    unsafe { socket.send_to(buf.as_ref(), host_addr).unwrap(); };
  });
}

#[test]
fn server_can_be_pinged() {
  server_test!((socket, host_addr) {
    unsafe {
      let mut buf = [1u8];
      socket.send_to(buf.as_ref(), host_addr).unwrap();

      let mut in_buf = [0u8, 256];
      let ms = Duration::span(|| {
        match socket.recv_from(&mut in_buf) {
          Ok((len, _src_addr)) => {
            assert_eq!(len, 1);
            assert_eq!(in_buf[0], 1);
          }

          Err(e) => panic!("Got error {} when trying to ping!", e)
        }
      });
      println!("Ping took {} ms!", ms.num_milliseconds());
    }
  });
}
<|endoftext|>


<commit_before>#![feature(iter_arith)]
#![feature(collections)]

extern crate byteorder;
extern crate crc;
extern crate unix_socket;
extern crate nix;
extern crate libc;

use std::path;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;

mod parser;
mod lvmetad;
mod pvlabel;
mod dm;
mod lv;
mod vg;
mod pv;

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use parser::LvmTextMap;

fn get_first_vg_meta() -> Result<(String, LvmTextMap)> {
    let dirs = vec![path::Path::new("/dev")];

    for pv in try!(pvlabel::scan_for_pvs(&dirs)) {
        let map = try!(pvlabel::textmap_from_dev(pv.as_path()));

        for (key, value) in map {
            match value {
                parser::Entry::TextMap(x) => return Ok((key, *x)),
                _ => {}
            }
        }
    }

    Err(Error::new(Other, "dude"))
}

fn main() {
    // println!("A");
    // let (name, mut map) = get_first_vg_meta().unwrap();
    // println!("B");
    // let vg = parser::vg_from_textmap(&name, &mut map);
    // println!("output {:?}", vg);

    match dm::list_devices() {
        Ok(x) => println!("{:?}", x),
        Err(x) => println!("error {}", x),
    }
}
<commit_msg>more fun test code in main.rs<commit_after>#![feature(iter_arith, result_expect)]

extern crate byteorder;
extern crate crc;
extern crate unix_socket;
extern crate nix;
extern crate libc;

use std::path;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;

use nix::fcntl;
use std::os::unix::io::AsRawFd;

mod parser;
mod lvmetad;
mod pvlabel;
mod dm;
mod lv;
mod vg;
mod pv;

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use parser::LvmTextMap;
use parser::TextMapOps;

fn get_first_vg_meta() -> Result<(String, LvmTextMap)> {
    let dirs = vec![path::Path::new("/dev")];

    for pv in try!(pvlabel::scan_for_pvs(&dirs)) {
        let map = try!(pvlabel::textmap_from_dev(pv.as_path()));

        for (key, value) in map {
            match value {
                parser::Entry::TextMap(x) => return Ok((key, *x)),
                _ => {}
            }
        }
    }

    Err(Error::new(Other, "dude"))
}

fn get_conf() -> Result<LvmTextMap> {
    use std::fs;
    use std::io::Read;

    let mut f = try!(fs::File::open("/etc/lvm/lvm.conf"));

    let mut buf = Vec::new();
    try!(f.read_to_end(&mut buf));

    parser::into_textmap(&buf)
}

fn main() {
    // println!("A");
    let (name, mut map) = get_first_vg_meta().unwrap();
    // println!("B");
    let vg = parser::vg_from_textmap(&name, &mut map).expect("didn't get vg!");
    println!("heyo {}", vg.extents_in_use());
    // println!("output {:?}", vg);

    // match dm::list_devices() {
    //     Ok(x) => println!("{:?}", x),
    //     Err(x) => println!("error {}", x),
    // }

    let tm = get_conf().unwrap();
    let global = tm.textmap_from_textmap("global").unwrap();

    println!("config = {:?}", global.i64_from_textmap("locking_type").unwrap());
}
<|endoftext|>


<commit_before>#![feature(path_ext)]
#![feature(collections)]

#[macro_use]
extern crate log;
extern crate git2;
extern crate iron;
extern crate toml;

use std::env;
use std::io::Read;
use std::fs::{self, PathExt, File};
use std::path::PathBuf;
use std::error::Error;

use git2::Repository;

use iron::prelude::*;

/// Contains various logger implementations for Grist.
///
/// TODO: log to a file in addition to the console
/// TODO: log to /var/log/
pub mod loggers;

/// Contains Iron server functions.
pub mod servers;

/// Contains code related to setting Grist's configuration.
///
/// Currently, configurations are set from a TOML configuration
/// file stored in `~/.gristconfig.toml`. Eventually, functionality
/// will be added to also allow configurations to be set from the
/// command-line, falling back to `~/.gristconfig.toml` and finally
/// to the defaults.
///
/// TODO: add command-line argument parsing
/// TODO: allow log file to be set from configs
pub mod config;

fn main() {

    // try to parse the config file at ~/.gristconfig.toml; otherwise, use defaults
    let configs = File::open("~/.gristconfig.toml")
        .and_then(|mut f: File| {
            let mut s = String::new();
            try!(f.read_to_string(&mut s));
            Ok(s) })
        .map_err(|err| String::from_str(err.description()) )
        .and_then(|s: String| config::parse_toml(s.as_ref()))
        .unwrap_or(config::DEFAULTS.clone());

    // set log level from configs
    let _ = if configs.debug {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Debug);
            Box::new(loggers::DebugLogger)
        });
    } else {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(loggers::DefaultLogger)
        });
    };

    let roots = configs.roots.unwrap_or(
        // get the current working dir, assume that we are in a valid directory
        vec![env::current_dir().unwrap()]
    );

    info!("Starting Grist in {:?}", roots);

    let repos: Vec<Repository> = roots.iter().flat_map(|ref root: &PathBuf| {
        fs::read_dir(root) // walk the root dir
            .unwrap()
            .filter_map( |entry| match entry {
                Ok(ref dir) if dir.path().is_dir() => { // found a dir
                    debug!("Found directory: {:?}", dir.path());
                    match Repository::init(dir.path()) { // attempt to open dir as git repo
                        Ok(repo) => { info!("Found repository: {:?}", dir.path()); Some(repo) },
                        Err(why) => { warn!("Failed to open {:?}: {}", dir.path(), why); None }
                    }
                },
                Err(why) => { warn!("Could not read entry: {}", why); None },
                Ok(ref e)=> { debug!("{:?} is not a directory.", e.path()); None }
            })
    }).collect();

    info!("Discovered {} repositories.", repos.len());

    // serve sample hello world page for now
    Iron::new(servers::hello_world).http("localhost:3000").unwrap();

}
<commit_msg>Reduce console log spam somewhat<commit_after>#![feature(path_ext)]
#![feature(collections)]

#[macro_use]
extern crate log;
extern crate git2;
extern crate iron;
extern crate toml;

use std::env;
use std::io::Read;
use std::fs::{self, PathExt, File};
use std::path::PathBuf;
use std::error::Error;

use git2::Repository;

use iron::prelude::*;

/// Contains various logger implementations for Grist.
///
/// TODO: log to a file in addition to the console
/// TODO: log to /var/log/
pub mod loggers;

/// Contains Iron server functions.
pub mod servers;

/// Contains code related to setting Grist's configuration.
///
/// Currently, configurations are set from a TOML configuration
/// file stored in `~/.gristconfig.toml`. Eventually, functionality
/// will be added to also allow configurations to be set from the
/// command-line, falling back to `~/.gristconfig.toml` and finally
/// to the defaults.
///
/// TODO: add command-line argument parsing
/// TODO: allow log file to be set from configs
pub mod config;

fn main() {

    // try to parse the config file at ~/.gristconfig.toml; otherwise, use defaults
    let configs = File::open("~/.gristconfig.toml")
        .and_then(|mut f: File| {
            let mut s = String::new();
            try!(f.read_to_string(&mut s));
            Ok(s) })
        .map_err(|err| String::from_str(err.description()) )
        .and_then(|s: String| config::parse_toml(s.as_ref()))
        .unwrap_or(config::DEFAULTS.clone());

    // set log level from configs
    let _ = if configs.debug {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Debug);
            Box::new(loggers::DebugLogger)
        });
    } else {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(loggers::DefaultLogger)
        });
    };

    let roots = configs.roots.unwrap_or(
        // get the current working dir, assume that we are in a valid directory
        vec![env::current_dir().unwrap()]
    );

    info!("Starting Grist in {:?}", roots);

    let repos: Vec<Repository> = roots.iter().flat_map(|ref root: &PathBuf| {
        fs::read_dir(root) // walk the root dir
            .unwrap() // we are assuming the directory is valid (for now)
            .filter_map( |entry| match entry {
                Ok(ref dir) if dir.path().is_dir() => { // found a dir
                    debug!("Found directory: {:?}", dir.path());
                    match Repository::init(dir.path()) { // attempt to open dir as git repo
                        Ok(repo) => { debug!("Found repository: {:?}", dir.path()); Some(repo) },
                        Err(why) => { warn!("Failed to open {:?}: {}", dir.path(), why); None }
                    }
                },
                Err(why) => { warn!("Could not read entry: {}", why); None },
                Ok(ref e)=> { debug!("{:?} is not a directory.", e.path()); None }
            })
    }).collect();

    info!("Discovered {} repositories.", repos.len());

    // serve sample hello world page for now
    Iron::new(servers::hello_world).http("localhost:3000").unwrap();

}
<|endoftext|>


<commit_before>extern crate image;
extern crate dungenon;

use std::io;
use std::path::PathBuf;

use dungenon::level::Level;
use dungenon::tile::Tile;
use dungenon::generator::MazeGen;

use image::RgbImage;
use image::Rgb;

fn main() {
    println!("Initializing level...");
    let mut level = init_level();

    println!("This is the dungenon-drawer. \nType Help for list of commands.");
    loop {
        println!("Enter command:");
        let mut command = string_from_cmd();
        println!("{}", command);
        match command.trim() {
            "dungeon" => carve_dungeon(&mut level),
            "maze" => carve_maze(&mut level),
            "room" => carve_rooms(&mut level),
            "reset" => level = init_level(),
            "export" => {
                png_export(&mut level);
            },
            "print" => print_level(&level),
            "help" => {
                println!("Help command is not implemented yet!");
            }
            "exit" => break,
            _ => println!("Invalid command"),
        }
    }
}

fn init_level() -> Level {
    println!("Input level width: ");
    let mut x = usize_from_cmd();

    println!("Input level height: ");
    let mut y = usize_from_cmd();

    Level::new_filled_with(Some(Tile::Wall), x, y)
}

fn carve_dungeon(level: &mut Level) {
    println!("Not implemented.");
}

fn carve_maze(level: &mut Level) {
    println!("Input MazeGen startpos x coordinate: ");
    let mut x = usize_from_cmd();

    println!("Input MazeGen startpos y coordinate: ");
    let mut y = usize_from_cmd();
    let mut mazegen = MazeGen::new(x,y);
    level.apply(|m| mazegen.generate(m));
}

fn carve_rooms(level: &mut Level) {
    println!("Not implemented.");
}

fn png_export(level: &mut Level) {
    let mut level_image = RgbImage::new(level.get_height() as u32, level.get_height() as u32);
    for y in 0 .. level.get_width() {
        for x in 0 .. level.get_height() {
            level_image.put_pixel(x as u32, y as u32, tile_to_color(&level[(x,y)]));
        }
    }
    let mut p = PathBuf::new();
    println!("Enter png name:");
    p.push(string_from_cmd().trim());
    p.set_extension("png");
    level_image.save(p.as_path());
}

fn tile_to_color(tile: &Option<Tile>) -> Rgb<u8> {
    match *tile {
        Some(ref tile) => {
            match *tile {
                Tile::Wall => image::Rgb([127u8, 127u8, 127u8]),
                Tile::Floor => image::Rgb([200u8, 200u8, 200u8]),
                Tile::Void => image::Rgb([0u8, 0u8, 0u8]),
            }
        },
        None => image::Rgb([0u8, 0u8, 0u8]),
    }
}

fn print_level(level: &Level) {
    let mut string = String::new();
    for y in 0 .. level.get_width() {
        for x in 0 .. level.get_height() {
            match level[(x,y)] {
                Some(ref tile) => {
                    match tile {
                        &Tile::Wall => string.push('#'),
                        &Tile::Floor => string.push(' '),
                        &Tile::Void => string.push('*'),
                    }
                },
                None => string.push('*'),
            }
        }
        string.push('\n');
    }
    println!("{}", &string);
}

fn usize_from_cmd() -> usize {
    let mut num = String::new();

    io::stdin().read_line(&mut num)
        .expect("failed to read line");

    let num: usize = num.trim().parse()
        .expect("Please type a number!");
    num
}

fn string_from_cmd() -> String {
    let mut string = String::new();
    io::stdin().read_line(&mut string)
    .expect("Failed to read line");
    string
}
<commit_msg>replaced image::Rgb with Rgb since I already have a use statement for the type<commit_after>extern crate image;
extern crate dungenon;

use std::io;
use std::path::PathBuf;

use dungenon::level::Level;
use dungenon::tile::Tile;
use dungenon::generator::MazeGen;

use image::RgbImage;
use image::Rgb;

fn main() {
    println!("Initializing level...");
    let mut level = init_level();

    println!("This is the dungenon-drawer. \nType Help for list of commands.");
    loop {
        println!("Enter command:");
        let mut command = string_from_cmd();
        println!("{}", command);
        match command.trim() {
            "dungeon" => carve_dungeon(&mut level),
            "maze" => carve_maze(&mut level),
            "room" => carve_rooms(&mut level),
            "reset" => level = init_level(),
            "export" => {
                png_export(&mut level);
            },
            "print" => print_level(&level),
            "help" => {
                println!("Help command is not implemented yet!");
            }
            "exit" => break,
            _ => println!("Invalid command"),
        }
    }
}

fn init_level() -> Level {
    println!("Input level width: ");
    let mut x = usize_from_cmd();

    println!("Input level height: ");
    let mut y = usize_from_cmd();

    Level::new_filled_with(Some(Tile::Wall), x, y)
}

fn carve_dungeon(level: &mut Level) {
    println!("Not implemented.");
}

fn carve_maze(level: &mut Level) {
    println!("Input MazeGen startpos x coordinate: ");
    let mut x = usize_from_cmd();

    println!("Input MazeGen startpos y coordinate: ");
    let mut y = usize_from_cmd();
    let mut mazegen = MazeGen::new(x,y);
    level.apply(|m| mazegen.generate(m));
}

fn carve_rooms(level: &mut Level) {
    println!("Not implemented.");
}

fn png_export(level: &mut Level) {
    let mut level_image = RgbImage::new(level.get_height() as u32, level.get_height() as u32);
    for y in 0 .. level.get_width() {
        for x in 0 .. level.get_height() {
            level_image.put_pixel(x as u32, y as u32, tile_to_color(&level[(x,y)]));
        }
    }
    let mut p = PathBuf::new();
    println!("Enter png name:");
    p.push(string_from_cmd().trim());
    p.set_extension("png");
    level_image.save(p.as_path());
}

fn tile_to_color(tile: &Option<Tile>) -> Rgb<u8> {
    match *tile {
        Some(ref tile) => {
            match *tile {
                Tile::Wall => Rgb([127u8, 127u8, 127u8]),
                Tile::Floor => Rgb([200u8, 200u8, 200u8]),
                Tile::Void => Rgb([0u8, 0u8, 0u8]),
            }
        },
        None => Rgb([0u8, 0u8, 0u8]),
    }
}

fn print_level(level: &Level) {
    let mut string = String::new();
    for y in 0 .. level.get_width() {
        for x in 0 .. level.get_height() {
            match level[(x,y)] {
                Some(ref tile) => {
                    match tile {
                        &Tile::Wall => string.push('#'),
                        &Tile::Floor => string.push(' '),
                        &Tile::Void => string.push('*'),
                    }
                },
                None => string.push('*'),
            }
        }
        string.push('\n');
    }
    println!("{}", &string);
}

fn usize_from_cmd() -> usize {
    let mut num = String::new();

    io::stdin().read_line(&mut num)
        .expect("failed to read line");

    let num: usize = num.trim().parse()
        .expect("Please type a number!");
    num
}

fn string_from_cmd() -> String {
    let mut string = String::new();
    io::stdin().read_line(&mut string)
    .expect("Failed to read line");
    string
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
//use std::fs::File;

extern crate time;

fn main() {

    // <134>2017-04-24T20:38:26Z cache-iad2645 fastly-logs-5-east[367774]: 54.87.185.35 "-" "GET
    // /npm/public/registry/i/is-utf8/_attachments/doc.json" 200 "npm/4.2.0 node/v7.8.0 linux x64"
    // "install" "26f82920d62b24e9" "HIT" "(null)" "cache-iad2645-IAD" "0" "1016" "1466" "251"

    // only quoted values have spaces.
    let mut value = String::new();
    let mut values = Vec::new();
    let mut state = "out";

    for byte in io::stdin().bytes() {
        let c = byte.unwrap() as char;

        if c == '\n' {
            // if i have a pending value add it to values
            if value.len() > 0 {
                values.push(value)
            }

            format(&values);

            value = String::new();
            values = Vec::new();
            continue;
        }

        if state == "out" {
            if c == '"' {
                state = "quoted";
                // skip quote
                continue;
            } else if c != ' ' {
                state = "value";
            }
        }

        if state == "value" {
            if c == ' ' {
                values.push(value);
                value = String::new();
                state = "out";
            } else if c != '"' {
                value.push(c);
            }
        }

        if state == "quoted" {
            if c == '"' {
                values.push(value);
                value = String::new();
                state = "out";
            } else {
                value.push(c);
            }
        }
    }
}

fn format(values: &Vec<String>) {
    let len = values.len();

    if len == 0 {
        return;
    }

    let date = parse_date(values[0].clone());
    let unixtime = logtime_to_unixtime(&date);
    let minute = unixtime - (unixtime % 60);

    // invalid time. wont count.
    if unixtime == 0 {
        return;
    }

    let pop = substr(&values[1], 6, 3);

    // if i have any values emit them.

    let service = path_to_service(&values[5]);

    let status = &values[6];

    let mut offset = 0;
    if len == 18 {
        offset = 1;
    }

    let egress_bytes = &values[15 + offset];
    let ingress_bytes = &values[16 + offset];

    for v in values {
        print!("{}, ", v);
    }

    println!("[{}]", len);

    println!("format {} {} {} {} {} {} {} {}",
             date,
             minute,
             unixtime,
             pop,
             service,
             status,
             egress_bytes,
             ingress_bytes);
}

fn parse_date(dateref: String) -> String {
    let mut date = dateref.clone();
    let offset_option = date.find('>');

    if offset_option != None {

        let offset = offset_option.unwrap() + 1;
        // -1 to remove the trailing Z
        let end = date.len() - offset - 1;

        //date = date.chars().skip(offset).take(end).collect();
        date = substr(&date, offset, end)
    }
    return date;
}

fn logtime_to_unixtime(date: &String) -> i64 {

    // 2017-04-24T20:38:26
    let unixtime = match time::strptime(date, "%Y-%m-%dT%H:%M:%S") {
        Ok(v) => v.to_timespec().sec,
        _ => 0,
    };

    return unixtime;
}

fn substr(string: &String, start: usize, len: usize) -> String {
    let copy = string.clone();
    return copy.chars().skip(start).take(len).collect();
}

fn path_to_service(path: &String) -> &'static str {

    if path.contains('@') {
        if path.contains(".tgz") {
            return "scoped-tarball";
        }
        return "scoped-json";
    }

    if path.contains("doc.json") {
        return "static-json";
    }

    if path.contains("doc.min.json") {
        return "corgi-json";
    }

    let len = path.len();

    if substr(&path, len - 4, 5) == ".tgz" {
        return "tarball";
    }

    if path.contains("/-/") {
        return "tie-fighter";
    }

    if !split_at(&path, ' ').contains('/') {
        return "json";
    }
    return "misc";
}

fn split_at(string: &String, find: char) -> String {
    return substr(&string, string.find(find).unwrap_or(0), string.len());
}
<commit_msg>feat(stdin): call lock only once, by explicitly doing so<commit_after>use std::io;
use std::io::prelude::*;
//use std::fs::File;

extern crate time;

fn main() {

    // <134>2017-04-24T20:38:26Z cache-iad2645 fastly-logs-5-east[367774]: 54.87.185.35 "-" "GET
    // /npm/public/registry/i/is-utf8/_attachments/doc.json" 200 "npm/4.2.0 node/v7.8.0 linux x64"
    // "install" "26f82920d62b24e9" "HIT" "(null)" "cache-iad2645-IAD" "0" "1016" "1466" "251"

    // only quoted values have spaces.
    let mut value = String::new();
    let mut values = Vec::new();
    let mut state = "out";

    let stdin = io::stdin();
    let handle = stdin.lock();

    for byte in handle.bytes() {
        let c = byte.unwrap() as char;

        if c == '\n' {
            // if i have a pending value add it to values
            if value.len() > 0 {
                values.push(value)
            }

            format(&values);

            value = String::new();
            values = Vec::new();
            continue;
        }

        if state == "out" {
            if c == '"' {
                state = "quoted";
                // skip quote
                continue;
            } else if c != ' ' {
                state = "value";
            }
        }

        if state == "value" {
            if c == ' ' {
                values.push(value);
                value = String::new();
                state = "out";
            } else if c != '"' {
                value.push(c);
            }
        }

        if state == "quoted" {
            if c == '"' {
                values.push(value);
                value = String::new();
                state = "out";
            } else {
                value.push(c);
            }
        }
    }
}

fn format(values: &Vec<String>) {
    let len = values.len();

    if len == 0 {
        return;
    }

    let date = parse_date(values[0].clone());
    let unixtime = logtime_to_unixtime(&date);
    let minute = unixtime - (unixtime % 60);

    // invalid time. wont count.
    if unixtime == 0 {
        return;
    }

    let pop = substr(&values[1], 6, 3);

    // if i have any values emit them.

    let service = path_to_service(&values[5]);

    let status = &values[6];

    let mut offset = 0;
    if len == 18 {
        offset = 1;
    }

    let egress_bytes = &values[15 + offset];
    let ingress_bytes = &values[16 + offset];

    for v in values {
        print!("{}, ", v);
    }

    println!("[{}]", len);

    println!("format {} {} {} {} {} {} {} {}",
             date,
             minute,
             unixtime,
             pop,
             service,
             status,
             egress_bytes,
             ingress_bytes);
}

fn parse_date(dateref: String) -> String {
    let mut date = dateref.clone();
    let offset_option = date.find('>');

    if offset_option != None {

        let offset = offset_option.unwrap() + 1;
        // -1 to remove the trailing Z
        let end = date.len() - offset - 1;

        //date = date.chars().skip(offset).take(end).collect();
        date = substr(&date, offset, end)
    }
    return date;
}

fn logtime_to_unixtime(date: &String) -> i64 {

    // 2017-04-24T20:38:26
    let unixtime = match time::strptime(date, "%Y-%m-%dT%H:%M:%S") {
        Ok(v) => v.to_timespec().sec,
        _ => 0,
    };

    return unixtime;
}

fn substr(string: &String, start: usize, len: usize) -> String {
    let copy = string.clone();
    return copy.chars().skip(start).take(len).collect();
}

fn path_to_service(path: &String) -> &'static str {

    if path.contains('@') {
        if path.contains(".tgz") {
            return "scoped-tarball";
        }
        return "scoped-json";
    }

    if path.contains("doc.json") {
        return "static-json";
    }

    if path.contains("doc.min.json") {
        return "corgi-json";
    }

    let len = path.len();

    if substr(&path, len - 4, 5) == ".tgz" {
        return "tarball";
    }

    if path.contains("/-/") {
        return "tie-fighter";
    }

    if !split_at(&path, ' ').contains('/') {
        return "json";
    }
    return "misc";
}

fn split_at(string: &String, find: char) -> String {
    return substr(&string, string.find(find).unwrap_or(0), string.len());
}
<|endoftext|>


<commit_before>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate xz2;
extern crate zip;

use std::fs;
use std::io;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;

struct OutputTo {
}

impl OutputTo {
    fn warn(&self, msg: String) {
        println!("TODO: {}", msg);
    }

    fn raw(&self, file: fs::File) -> io::Result<()> {
        unimplemented!();
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a>(fd: &mut Box<io::BufRead + 'a>) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 3
        && b'B' == header[0] && b'Z' == header[1] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

fn plus<T: Clone>(vec: &Vec<T>, thing: T) -> Vec<T> {
    let mut ret = vec.clone();
    ret.push(thing);
    ret
}

struct BoxReader<'a> {
    fd: Box<io::BufRead + 'a>
}

impl<'a> io::Read for BoxReader<'a> {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        self.fd.read(&mut buf)
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

fn unpack_or_raw<'a, F>(
    fd: Box<io::BufRead + 'a>,
    path: Vec<String>,
    output: &OutputTo,
    fun: F) -> io::Result<()>
where F: FnOnce(Box<io::BufRead + 'a>) -> io::Result<Box<io::BufRead + 'a>>
{
    let mut temp = tempfile()?;

    match fun(fd).map(|stream| io::copy(&mut BoxReader { fd: stream }, &mut temp)) {
        Ok(_) => {
            temp.seek(io::SeekFrom::Start(0))?;
            unpack(Box::new(io::BufReader::new(temp)), path, output)
        },
        Err(e) => {
            output.warn(format!(
                    "thought we could unpack '{:?}' but we couldn't: {}",
                    path, e));
            output.raw(temp)
        }
    }
}

fn unpack<'a>(mut fd: Box<io::BufRead + 'a>, path: Vec<String>, output: &OutputTo) -> io::Result<()> {
    match identify(&mut fd)? {
        FileType::GZip => {
            unpack_or_raw(fd, path, output,
                |fd| gzip::Decoder::new(fd).map(
                    |dec| Box::new(io::BufReader::new(dec)) as Box<io::BufRead>))
        },
        FileType::Ar if path.last().unwrap().ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd);
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_path = plus(&path, entry.header().identifier().to_string());
                unpack(Box::new(io::BufReader::new(entry)), new_path, output)?;
            }
            Ok(())
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd);
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_path = plus(&path, entry.path()?.to_str().expect("valid utf-8").to_string());
                unpack(Box::new(io::BufReader::new(entry)), new_path, output)?;
            }
            Ok(())
        },
        FileType::Xz => {
            unpack(Box::new(io::BufReader::new(xz2::bufread::XzDecoder::new(fd))), path, output)
        },
        FileType::Zip => {
            let mut angry = BoxReader { fd };
            while let Some(mut entry) = zip::read::read_single(&mut angry)? {
                let new_path = plus(&path, (&*entry.name).to_string());
                let reader = entry.get_reader();
                let unpacked = unpack(Box::new(io::BufReader::new(reader)), new_path, output);
                if let Err(e) = unpacked {
                    if e.kind() == io::ErrorKind::InvalidInput {
                        break;
                    } else {
                        return Err(e)
                    };
                }
            }
            let bytes_at_end = count_bytes(&mut angry)?;
            if 0 != bytes_at_end {
                println!("{:?}: discarding trailing bytes: {}", path, bytes_at_end);
            }
            Ok(())
        },
        other => {
            println!("{:?}: {:?} {}", path, other, count_bytes(&mut BoxReader { fd })?);
            Ok(())
        },
    }
}

fn main() {
    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();
    let output = OutputTo {};
    for path in matches.values_of("INPUT").unwrap() {
        let file = fs::File::open(path).unwrap();
        let read = io::BufReader::new(file);
        unpack(Box::new(read), vec![path.to_string()], &output).unwrap();
    }
}

<commit_msg>move path into a slist in output<commit_after>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate xz2;
extern crate zip;

use std::fs;
use std::io;
use std::rc::Rc;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;

#[derive(Debug)]
struct Node<T> {
    next: Option<Rc<Node<T>>>,
    value: T,
}

impl<T> Node<T> {
    fn head(obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: None,
            value: obj,
        })
    }

    fn plus(what: &Rc<Node<T>>, obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: Some(what.clone()),
            value: obj
        })
    }
}

struct OutputTo {
    path: Rc<Node<String>>,
}

impl OutputTo {
    fn warn(&self, msg: String) {
        println!("TODO: {}", msg);
    }

    fn raw(&self, file: fs::File) -> io::Result<()> {
        unimplemented!();
    }
}

impl OutputTo {
    fn with_path(&self, path: String) -> OutputTo {
        OutputTo {
            path: Node::plus(&self.path, path)
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a>(fd: &mut Box<io::BufRead + 'a>) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 3
        && b'B' == header[0] && b'Z' == header[1] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

fn plus<T: Clone>(vec: &Vec<T>, thing: T) -> Vec<T> {
    let mut ret = vec.clone();
    ret.push(thing);
    ret
}

struct BoxReader<'a> {
    fd: Box<io::BufRead + 'a>
}

impl<'a> io::Read for BoxReader<'a> {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        self.fd.read(&mut buf)
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

fn unpack_or_raw<'a, F>(
    fd: Box<io::BufRead + 'a>,
    output: &OutputTo,
    fun: F) -> io::Result<()>
where F: FnOnce(Box<io::BufRead + 'a>) -> io::Result<Box<io::BufRead + 'a>>
{
    let mut temp = tempfile()?;

    match fun(fd).map(|stream| io::copy(&mut BoxReader { fd: stream }, &mut temp)) {
        Ok(_) => {
            temp.seek(io::SeekFrom::Start(0))?;
            unpack(Box::new(io::BufReader::new(temp)), output)
        },
        Err(e) => {
            output.warn(format!(
                    "thought we could unpack '{:?}' but we couldn't: {}",
                    output.path, e));
            output.raw(temp)
        }
    }
}

fn unpack<'a>(mut fd: Box<io::BufRead + 'a>, output: &OutputTo) -> io::Result<()> {
    match identify(&mut fd)? {
        FileType::GZip => {
            unpack_or_raw(fd, output,
                |fd| gzip::Decoder::new(fd).map(
                    |dec| Box::new(io::BufReader::new(dec)) as Box<io::BufRead>))
        },
        FileType::Ar if output.path.value.ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd);
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_output = output.with_path(entry.header().identifier().to_string());
                unpack(Box::new(io::BufReader::new(entry)), &new_output)?;
            }
            Ok(())
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd);
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_output = output.with_path(entry.path()?.to_str().expect("valid utf-8").to_string());
                unpack(Box::new(io::BufReader::new(entry)), &new_output)?;
            }
            Ok(())
        },
        FileType::Xz => {
            unpack(Box::new(io::BufReader::new(xz2::bufread::XzDecoder::new(fd))), output)
        },
        FileType::Zip => {
            let mut angry = BoxReader { fd };
            while let Some(mut entry) = zip::read::read_single(&mut angry)? {
                let new_output = output.with_path((&*entry.name).to_string());
                let reader = entry.get_reader();
                let unpacked = unpack(Box::new(io::BufReader::new(reader)), &new_output);
                if let Err(e) = unpacked {
                    if e.kind() == io::ErrorKind::InvalidInput {
                        break;
                    } else {
                        return Err(e)
                    };
                }
            }
            let bytes_at_end = count_bytes(&mut angry)?;
            if 0 != bytes_at_end {
                println!("{:?}: discarding trailing bytes: {}", output.path, bytes_at_end);
            }
            Ok(())
        },
        other => {
            println!("{:?}: {:?} {}", output.path, other, count_bytes(&mut BoxReader { fd })?);
            Ok(())
        },
    }
}

fn main() {
    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();
    for path in matches.values_of("INPUT").unwrap() {
        let file = fs::File::open(path).unwrap();
        let read = io::BufReader::new(file);
        unpack(Box::new(read), &OutputTo {
            path: Node::head(path.to_string())
        }).unwrap();
    }
}

<|endoftext|>