<commit_before>#[macro_use(crate_version)]
extern crate clap;
extern crate semver;

use clap::{App, AppSettings, SubCommand, Arg, ArgGroup, Values};
use std::process::{exit, Command, Stdio};
use semver::{Version, VersionReq, Identifier};

/// Information on the rustc compiler version in this environment
struct RustCInfo {
    channel: String,
    version: Version,
}

impl RustCInfo {
    /// Obtains the rust compiler info
    fn get_info() -> RustCInfo {
        // Get RustC version from command output
        let output = Command::new("rustc")
            .arg("-V")
            .stdin(Stdio::null())
            .stderr(Stdio::inherit())
            .output()
            .expect("Failed to get rustc version");
        let output_str = String::from_utf8_lossy(&output.stdout);

        // Parse the string and get the version portion
        let verstr = output_str.split_whitespace()
            .nth(1)
            .expect("Failed to get rustc version string");
        let version = Version::parse(verstr).expect("Failed to parse rustc version");

        // Get channel from pre-release portion of version
        let channel = match version.pre.iter().next() {
            Some(ident) => {
                match ident {
                    &Identifier::AlphaNumeric(ref s) => s.clone(),
                    &Identifier::Numeric(_) => "unknown".to_string(),
                }
            }
            None => "stable".to_string(),
        };
        // Strip pre-release from version
        let version = Version {
            major: version.major,
            minor: version.minor,
            patch: version.patch,
            pre: vec![],
            build: vec![],
        };

        RustCInfo {
            channel: channel,
            version: version,
        }
    }

    fn matches_channel(&self, channel: &str) -> bool {
        self.channel == channel
    }

    fn matches_version(&self, version: &str) -> bool {
        let versreq = VersionReq::parse(version).expect("Invalid version argument");
        versreq.matches(&self.version)
    }

    fn matches_any<'a, 'b>(&'a self,
                           channels: Option<Values<'b>>,
                           versions: Option<Values<'b>>)
                           -> bool {
        channels.map_or(true,
                        |mut channels| channels.any(|ch| self.matches_channel(ch))) &&
        versions.map_or(true,
                        |mut versions| versions.any(|v| self.matches_version(v)))
    }
}

fn main() {
    // CLI
    let matches = App::new("cargo when")
                    .bin_name("cargo")
                    .about("Runs other cargo commands conditionally upon rustc version.")
                    .version(crate_version!())
                    .setting(AppSettings::SubcommandRequiredElseHelp)
                    //.setting(AppSettings::GlobalVersion)
                    .subcommand(SubCommand::with_name("when")
                        .usage("cargo when [OPTIONS] <CARGO SUBCOMMAND> [SUBCOMMAND OPTIONS]")
                        .about(concat!("Runs subsequent cargo command only when the specified ",
                                        "options match the current rustc version."))
                        .after_help(concat!("To specify a set of multiple possible matches for an ",
                                            "option, separate the values by a comma and no ",
                                            "spaces. At least one match option is required. If ",
                                            "multiple match options are present, each option ",
                                            "specifies an additional match requirement for any of ",
                                            "the set of possible values for that option."))
                        .setting(AppSettings::ArgRequiredElseHelp)
                        .setting(AppSettings::AllowExternalSubcommands)
                        .group(ArgGroup::with_name("matches")
                            .required(true)
                            .multiple(true)
                        )
                        .arg(Arg::with_name("CHANNEL")
                            .short("c")
                            .long("channel")
                            .group("matches")
                            .help("Matches rustc release channel(s)")
                            .takes_value(true)
                            .possible_values(&["stable", "beta", "nightly"])
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("VERSION")
                            .short("v")
                            .long("version")
                            .group("matches")
                            .help(concat!("Matches rustc version(s) using same rules and version ",
                                            "syntax as Cargo"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                    )
                    .subcommand(SubCommand::with_name("unless")
                        .usage("cargo unless [OPTIONS] <CARGO SUBCOMMAND> [SUBCOMMAND OPTIONS]")
                        .about(concat!("Runs subsequent cargo command except when the specified ",
                                        "options match the current rustc version. This is the ",
                                        "negation of 'cargo when'"))
                        .after_help(concat!("To specify a set of multiple possible matches for an ",
                                            "option, separate the values by a comma and no ",
                                            "spaces. At least one match option is required. If ",
                                            "multiple match options are present, each option ",
                                            "specifies an additional match requirement for any of ",
                                            "the set of possible values for that option."))
                        .setting(AppSettings::ArgRequiredElseHelp)
                        .setting(AppSettings::AllowExternalSubcommands)
                        .group(ArgGroup::with_name("matches")
                            .required(true)
                            .multiple(true)
                        )
                        .arg(Arg::with_name("CHANNEL")
                            .short("c")
                            .long("channel")
                            .group("matches")
                            .help("Matches rustc release channel(s)")
                            .takes_value(true)
                            .possible_values(&["stable", "beta", "nightly"])
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("VERSION")
                            .short("v")
                            .long("version")
                            .group("matches")
                            .help(concat!("Matches rustc version(s) using same rules and version ",
                                            "syntax as Cargo"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                    ).get_matches();

    // Query rustc version
    let rustc_info = RustCInfo::get_info();

    // Check conditions, gets command if matches, None if not
    let command = match matches.subcommand() {
        ("when", Some(sub)) => {
            // Check args for any matches
            if rustc_info.matches_any(sub.values_of("CHANNEL"), sub.values_of("VERSION")) {

                match sub.subcommand() {
                    (external, Some(extm)) => {
                        let mut cmd = vec![external];
                        if let Some(vals) = extm.values_of("") {
                            cmd.extend(vals);
                        }
                        Some(cmd)
                    }
                    _ => {
                        println!("{}", sub.usage());
                        exit(1);
                    }
                }
            } else {
                None
            }
        }
        ("unless", Some(sub)) => {
            // Check args for any matches, just negation of when
            if !rustc_info.matches_any(sub.values_of("CHANNEL"), sub.values_of("VERSION")) {
                match sub.subcommand() {
                    (external, Some(extm)) => {
                        let mut cmd = vec![external];
                        if let Some(vals) = extm.values_of("") {
                            cmd.extend(vals);
                        }
                        Some(cmd)
                    }
                    _ => {
                        println!("{}", sub.usage());
                        exit(1);
                    }
                }
            } else {
                None
            }
        }
        _ => None,
    };

    // Now run the chain cargo command if we're a match
    if let Some(args) = command {
        let status = Command::new("cargo")
            .args(&args)
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status();

        match status {
            Ok(s) => {
                if !s.success() {
                    exit(s.code().unwrap_or(127));
                }
            }
            Err(_) => {
                exit(127);
            }
        }
    }
}
<commit_msg>feat: exists&equals options for env var matches<commit_after>#[macro_use(crate_version)]
extern crate clap;
extern crate semver;

use std::env;
use std::io::prelude::*;
use clap::{App, AppSettings, SubCommand, Arg, ArgGroup, Values};
use std::process::{exit, Command, Stdio};
use semver::{Version, VersionReq, Identifier, ReqParseError};

/// Information on the rustc compiler version in this environment
struct RustCInfo {
    channel: String,
    version: Version,
}

impl RustCInfo {
    /// Obtains the rust compiler info
    fn get_info() -> RustCInfo {
        // Get RustC version from command output
        let output = Command::new("rustc")
            .arg("-V")
            .stdin(Stdio::null())
            .stderr(Stdio::inherit())
            .output()
            .expect("Failed to get rustc version");
        let output_str = String::from_utf8_lossy(&output.stdout);

        // Parse the string and get the version portion
        let verstr = output_str.split_whitespace()
            .nth(1)
            .expect("Failed to get rustc version string");
        let version = Version::parse(verstr).expect("Failed to parse rustc version");

        // Get channel from pre-release portion of version
        let channel = match version.pre.iter().next() {
            Some(ident) => {
                match ident {
                    &Identifier::AlphaNumeric(ref s) => s.clone(),
                    &Identifier::Numeric(_) => "unknown".to_string(),
                }
            }
            None => "stable".to_string(),
        };
        // Strip pre-release from version
        let version = Version {
            major: version.major,
            minor: version.minor,
            patch: version.patch,
            pre: vec![],
            build: vec![],
        };

        RustCInfo {
            channel: channel,
            version: version,
        }
    }

    /// Does a string match the rust compiler's channel
    fn matches_channel(&self, channel: &str) -> bool {
        self.channel == channel.to_lowercase()
    }

    /// Does the rust compiler version match a version requirement string
    fn matches_version(&self, version: &str) -> Result<bool, ReqParseError> {
        let versreq = try!(VersionReq::parse(version));
        Ok(versreq.matches(&self.version))
    }

    /// Do any of the channel values match?
    fn matches_any_channels<'a, 'b>(&'a self,
                                    channels: Option<Values<'b>>,
                                    when_none: bool)
                                    -> bool {
        channels.map_or(when_none,
                        |mut channels| channels.any(|ch| self.matches_channel(ch)))
    }

    /// Do any of the version values match?
    fn matches_any_versions<'a, 'b>(&'a self,
                                    versions: Option<Values<'b>>,
                                    when_none: bool)
                                    -> Result<bool, ReqParseError> {
        match versions {
            Some(vers) => {
                for v in vers {
                    if try!(self.matches_version(v)) {
                        return Ok(true);
                    }
                }
                Ok(false)
            }
            None => Ok(when_none)
        }
    }
}

/// A parsed environment variable requirement such as 'RUST_SRC_PATH=~/rustsrc'
struct EnvVarReq<'a> {
    name: &'a str,
    value: &'a str,
}

impl<'a> EnvVarReq<'a> {
    /// Parse and validate the environment variable requirement string
    fn parse(var: &'a str) -> Result<EnvVarReq<'a>, String> {
        let mut split = var.splitn(2, '=');

        let name = try!(split.next().ok_or("Environment variable requirement has no name"));
        if name.is_empty() {
            return Err("Invalid environment variable name".to_string());
        }
        let value = try!(split.next()
            .ok_or(format!("Invalid environment variable requirement, expecting: {}=VALUE",
                           name)));

        Ok(EnvVarReq {
            name: name,
            value: value,
        })
    }

    /// Does the requirement match an environment variable value?
    fn matches(&self) -> bool {
        // Don't try to convert OsString to unicode, just conver unicode to OsString.
        env::var_os(self.name).map_or(false, |v| v == *self.value)
    }
}

/// Do any of the environment variables exist?
fn any_env_vars_exist<'a>(vars: Option<Values<'a>>, when_none: bool) -> bool {
    vars.map_or(when_none, |mut vars| vars.any(|v| env::var_os(v).is_some()))
}

/// Do any of the environment variable values match?
fn matches_any_env_vars<'a>(vars: Option<Values<'a>>, when_none: bool) -> Result<bool, String> {
    match vars {
        Some(v) => {
            for var in v {
                let req = try!(EnvVarReq::parse(var));
                if req.matches() {
                    return Ok(true);
                }
            }
            Ok(false)
        }
        None => Ok(when_none),
    }
}

fn main() {
    // CLI
    let matches = App::new("cargo when")
                    .bin_name("cargo")
                    .about(concat!("Runs other cargo commands conditionally upon rust compiler ",
                                    "version and environment."))
                    .version(crate_version!())
                    .setting(AppSettings::SubcommandRequiredElseHelp)
                    //.setting(AppSettings::GlobalVersion)
                    .subcommand(SubCommand::with_name("when")
                        .usage("cargo when [OPTIONS] <CARGO SUBCOMMAND> [SUBCOMMAND OPTIONS]")
                        .about(concat!("Runs subsequent cargo command only when the specified ",
                                        "options match the current rust compiler version and ",
                                        "environment."))
                        .after_help(concat!("To specify a set of multiple possible matches for an ",
                                            "option, separate the values by a comma and no ",
                                            "spaces. At least one match option is required. If ",
                                            "multiple match options are present, each option ",
                                            "specifies an additional match requirement for any of ",
                                            "the set of possible values for that option."))
                        .setting(AppSettings::ArgRequiredElseHelp)
                        .setting(AppSettings::AllowExternalSubcommands)
                        .group(ArgGroup::with_name("matches")
                            .required(true)
                            .multiple(true)
                        )
                        .arg(Arg::with_name("CHANNEL")
                            .short("c")
                            .long("channel")
                            .group("matches")
                            .help("Matches rustc release channel(s)")
                            .takes_value(true)
                            .possible_values(&["stable", "beta", "nightly"])
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("VERSION")
                            .short("v")
                            .long("version")
                            .group("matches")
                            .help(concat!("Matches rustc version(s) using same rules and version ",
                                            "syntax as Cargo"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("ENV-VARIABLE")
                            .short("x")
                            .long("exists")
                            .group("matches")
                            .help("Matches when environment variable(s) exist")
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("ENV-VARIABLE=VALUE")
                            .short("e")
                            .long("equals")
                            .group("matches")
                            .help(concat!("Matches when environment variable(s) equals specified ",
                                            "value(s), e.g. RUST_SRC_PATH=~/rustsrc"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                    )
                    .subcommand(SubCommand::with_name("unless")
                        .usage("cargo unless [OPTIONS] <CARGO SUBCOMMAND> [SUBCOMMAND OPTIONS]")
                        .about(concat!("Runs subsequent cargo command except when the specified ",
                                        "options match the current rust compiler version and ",
                                        "environment. This is the negation of 'cargo when'."))
                        .after_help(concat!("To specify a set of multiple possible matches for an ",
                                            "option, separate the values by a comma and no ",
                                            "spaces. At least one match option is required. If ",
                                            "multiple match options are present, each option ",
                                            "specifies an additional match requirement for any of ",
                                            "the set of possible values for that option."))
                        .setting(AppSettings::ArgRequiredElseHelp)
                        .setting(AppSettings::AllowExternalSubcommands)
                        .group(ArgGroup::with_name("matches")
                            .required(true)
                            .multiple(true)
                        )
                        .arg(Arg::with_name("CHANNEL")
                            .short("c")
                            .long("channel")
                            .group("matches")
                            .help("Matches rustc release channel(s)")
                            .takes_value(true)
                            .possible_values(&["stable", "beta", "nightly"])
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("VERSION")
                            .short("v")
                            .long("version")
                            .group("matches")
                            .help(concat!("Matches rustc version(s) using same rules and version ",
                                            "syntax as Cargo"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("ENV-VARIABLE")
                            .short("x")
                            .long("exists")
                            .group("matches")
                            .help("Matches when environment variable(s) exist")
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                        .arg(Arg::with_name("ENV-VARIABLE=VALUE")
                            .short("e")
                            .long("equals")
                            .group("matches")
                            .help(concat!("Matches when environment variable(s) equals specified ",
                                            "value(s), e.g. RUST_SRC_PATH=~/rustsrc"))
                            .takes_value(true)
                            .min_values(1)
                            .require_delimiter(true)
                        )
                    ).get_matches();

    // Query rustc version
    let rustc_info = RustCInfo::get_info();

    // Check conditions, gets command if matches, None if not
    let command = match matches.subcommand() {
        ("when", Some(sub)) => {
            // Do all the provided match options match the current compiler and environment?
            let env_matches = matches_any_env_vars(sub.values_of("ENV-VARIABLE=VALUE"), true);
            if env_matches.is_err() {
                writeln!(std::io::stderr(), "{}", env_matches.unwrap_err()).ok();
                writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                exit(1);
            }

            let vers_matches = rustc_info.matches_any_versions(sub.values_of("VERSION"), true);
            if vers_matches.is_err() {
                writeln!(std::io::stderr(), "{}", vers_matches.unwrap_err()).ok();
                writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                exit(1);
            }

            if rustc_info.matches_any_channels(sub.values_of("CHANNEL"), true) &&
               vers_matches.unwrap_or(false) &&
               any_env_vars_exist(sub.values_of("ENV-VARIABLE"), true) &&
               env_matches.unwrap_or(false) {

                match sub.subcommand() {
                    (external, Some(extm)) => {
                        let mut cmd = vec![external];
                        if let Some(vals) = extm.values_of("") {
                            cmd.extend(vals);
                        }
                        Some(cmd)
                    }
                    _ => {
                        // No cargo subcommand was provided, print usage help message and exit
                        writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                        exit(1);
                    }
                }
            } else {
                None
            }
        }
        ("unless", Some(sub)) => {
            // Do none of the provided match options match the current compiler and environment?
            let env_matches = matches_any_env_vars(sub.values_of("ENV-VARIABLE=VALUE"), false);
            if env_matches.is_err() {
                writeln!(std::io::stderr(), "{}", env_matches.unwrap_err()).ok();
                writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                exit(1);
            }

            let vers_matches = rustc_info.matches_any_versions(sub.values_of("VERSION"), false);
            if vers_matches.is_err() {
                writeln!(std::io::stderr(), "{}", vers_matches.unwrap_err()).ok();
                writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                exit(1);
            }

            if !rustc_info.matches_any_channels(sub.values_of("CHANNEL"), false) &&
               !vers_matches.unwrap_or(true) &&
               !any_env_vars_exist(sub.values_of("ENV-VARIABLE"), false) &&
               !env_matches.unwrap_or(true) {

                match sub.subcommand() {
                    (external, Some(extm)) => {
                        let mut cmd = vec![external];
                        if let Some(vals) = extm.values_of("") {
                            cmd.extend(vals);
                        }
                        Some(cmd)
                    }
                    _ => {
                        // No cargo subcommand was provided, print usage help message and exit
                        writeln!(std::io::stderr(), "{}", sub.usage()).ok();
                        exit(1);
                    }
                }
            } else {
                None
            }
        }
        _ => None,
    };

    // If we're a match, the chained cargo command will be provided, otherwise, we do nothing
    if let Some(args) = command {
        let status = Command::new("cargo")
            .args(&args)
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .status();

        match status {
            Ok(s) => {
                if !s.success() {
                    exit(s.code().unwrap_or(127));
                }
            }
            Err(_) => {
                exit(127);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate sonos_discovery;

use sonos_discovery::Discover;

fn main() {
    let discovery = Discover::new().unwrap();
    let devices = discovery.start(None, Some(3)).unwrap();
    for device in devices {
        println!("{:?}", device)
    }
}
<commit_msg>Add elapsed time to binary<commit_after>extern crate sonos_discovery;

use sonos_discovery::Discover;
use std::time::Instant;

fn main() {
    let start_time = Instant::now();

    let discovery = Discover::new().unwrap();
    let ips = discovery.start(None, Some(3)).unwrap();
    for ip in ips {
        println!("{:?}", ip)
    }

    println!("\nTime: {:?}", start_time.elapsed())
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate rustc_serialize;
extern crate docopt;
extern crate glob;
extern crate toml;

use docopt::Docopt;
use std::path::Path;
use std::fs::File;
use std::fs::metadata;
use std::io::prelude::*;

const USAGE: &'static str = "
enforcer for code rules

Usage:
  enforcer <glob> [-c|--clean]
  enforcer (-h | --help)
  enforcer --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --count       only count found entries
  -c --clean    clean up trailing whitespaces
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_count: bool,
    flag_clean: bool,
    arg_glob: String,
}
const HAS_TABS: u8               = 1 << 0;
const TRAILING_SPACES: u8        = 1 << 1;
const HAS_ILLEGAL_CHARACTERS: u8 = 1 << 2;

fn check_content<'a>(input: &'a str) -> std::io::Result<u8> {
    let mut result = 0;
    for line in input.lines_any() {
        if line.ends_with(' ') {
            result |= TRAILING_SPACES;
        }
        if line.contains("\t") {
            result |= HAS_TABS;
        }
        if line.as_bytes().iter().any(|x| *x > 127) {
            result |= HAS_ILLEGAL_CHARACTERS;
        }
    }
    Ok(result)
}

// fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
//     if input.contains(' ') {
//         input
//         .chars()
//         .filter(|&x| x != ' ')
//         .collect::<std::string::String>()
//         .into()
//     } else {
//         input.into()
//     }
// }

fn clean_string(input: &str) -> std::io::Result<String> {
    let v: Vec<&str> = input
        .lines_any()
        .map(|line| line.trim_right())
        .collect();

    Ok(if input.ends_with("\n") {
            v.join("\n") + "\n"
        } else {
            v.join("\n")
        })
}

fn is_dir(path: &Path) -> bool {
    if let Ok(result) = metadata(&path) {
        result.is_dir()
    } else {
        false
    }
}

fn check_path(path: &Path, clean: bool) -> std::io::Result<()> {
    use std::io::ErrorKind;

    let mut f = try!(File::open(path));
    let mut buffer = String::new();
    let mut check = 0;
    if let Err(e) = f.read_to_string(&mut buffer) {
        match e.kind() {
            ErrorKind::InvalidData => check = check | HAS_ILLEGAL_CHARACTERS,
            _ => return Err(e),
        }
    }
    // only check content if we could read the file
    if check == 0 { check = try!(check_content(&buffer)); }
    if (check & HAS_ILLEGAL_CHARACTERS) > 0 {
        println!("HAS_ILLEGAL_CHARACTERS:[{}]", path.display());
    }
    if (check & HAS_TABS) > 0 {
        println!("HAS_TABS:[{}]", path.display());
    }
    if (check & TRAILING_SPACES) > 0 {
        println!("TRAILING_SPACES:[{}]", path.display());
        if clean {
            println!("cleaning trailing whitespaces");
            let res_string = try!(clean_string(&buffer));
            let mut file = try!(File::create(path));
            try!(file.write_all(res_string.as_bytes()));
        }
    }
    Ok(())
}

struct EnforcerCfg {
    unwanted: Vec<String>,
}

fn load_config<'a>(input: &'a str) -> std::io::Result<EnforcerCfg> {
    use std::io::{Error, ErrorKind};

    let mut parser = toml::Parser::new(input);
    match parser.parse() {
        Some(toml) => {
            let toignore = toml["ignore"].as_slice().unwrap();
            let mut xs = Vec::new();
            for i in toignore {
                xs.push(i.as_str().unwrap().to_string());
            }
            return Ok(EnforcerCfg { unwanted: xs })
        },
        None => {
            return Err(Error::new(ErrorKind::Other, "oh no!"));
        }
    };
}

#[allow(dead_code)]
fn main() {
    use glob::glob;
    use std::ffi::OsStr;
    env_logger::init().unwrap();

    fn get_cfg() -> EnforcerCfg {
        fn read_unwanted() -> std::io::Result<EnforcerCfg> {
            let mut cfg_file = try!(File::open(".enforcer"));
            let mut buffer = String::new();
            try!(cfg_file.read_to_string(&mut buffer));
            load_config(&buffer[..])
        }
        let default_cfg = EnforcerCfg { unwanted: vec![".git".to_string(), ".bake".to_string()]};
        let unwanted_cfg = read_unwanted()
            .unwrap_or(default_cfg);
        println!("loaded ignores: {:?}", unwanted_cfg.unwanted);
        unwanted_cfg
    }

    fn is_unwanted(path: &OsStr, unwanted_cfg: &EnforcerCfg) -> bool {
        unwanted_cfg.unwanted.iter().any(|x| path.to_os_string().into_string().unwrap() == *x)
    }

    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let pat = args.arg_glob.to_string();

    let unwanted_cfg = get_cfg();
    for path in glob(&*pat).unwrap()
        .filter_map(Result::ok)
        .filter(|x| !x.components().any(|y| is_unwanted(y.as_os_str(), &unwanted_cfg))) {
            if !is_dir(path.as_path()) {
                check_path(path.as_path(), args.flag_clean)
                    .ok()
                    .expect(&format!("check_path for {:?} should work", path));
            }
    }
}

#[cfg(test)]
mod tests {
    use super::clean_string;
    use super::load_config;

    #[test]
    fn test_clean_does_not_remove_trailing_newline() {
        let content = "1\n2\n3\n4\n5\n";
        let cleaned = clean_string(content).unwrap();
        assert!(cleaned.eq(content));
    }
    #[test]
    fn test_clean_trailing_whitespace() {
        let content = "1 \n2";
        let cleaned = clean_string(content).unwrap();
        println!("{:?}", cleaned);
        assert!(cleaned.eq("1\n2"));
    }
    #[test]
    fn test_load_simple_config() {
        println!("starting....");
        let c = include_str!("../samples/.enforcer");
        println!("starting2....{}", c);
        let cfg = load_config(c).unwrap();
        println!("{:?}", cfg.unwanted);
    }
}

<commit_msg>extracted function to handle enforcer config stuff,<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate rustc_serialize;
extern crate docopt;
extern crate glob;
extern crate toml;

use docopt::Docopt;
use std::path::Path;
use std::fs::File;
use std::fs::metadata;
use std::io::prelude::*;
use glob::Pattern;

const USAGE: &'static str = "
enforcer for code rules

Usage:
  enforcer <glob> [-c|--clean]
  enforcer (-h | --help)
  enforcer --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --count       only count found entries
  -c --clean    clean up trailing whitespaces
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_count: bool,
    flag_clean: bool,
    arg_glob: String,
}
const HAS_TABS: u8               = 1 << 0;
const TRAILING_SPACES: u8        = 1 << 1;
const HAS_ILLEGAL_CHARACTERS: u8 = 1 << 2;

fn check_content<'a>(input: &'a str) -> std::io::Result<u8> {
    let mut result = 0;
    for line in input.lines_any() {
        if line.ends_with(' ') {
            result |= TRAILING_SPACES;
        }
        if line.contains("\t") {
            result |= HAS_TABS;
        }
        if line.as_bytes().iter().any(|x| *x > 127) {
            result |= HAS_ILLEGAL_CHARACTERS;
        }
    }
    Ok(result)
}

// fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
//     if input.contains(' ') {
//         input
//         .chars()
//         .filter(|&x| x != ' ')
//         .collect::<std::string::String>()
//         .into()
//     } else {
//         input.into()
//     }
// }

fn clean_string(input: &str) -> std::io::Result<String> {
    let v: Vec<&str> = input
        .lines_any()
        .map(|line| line.trim_right())
        .collect();

    Ok(if input.ends_with("\n") {
            v.join("\n") + "\n"
        } else {
            v.join("\n")
        })
}

fn is_dir(path: &Path) -> bool {
    if let Ok(result) = metadata(&path) {
        result.is_dir()
    } else {
        false
    }
}

fn check_path(path: &Path, clean: bool) -> std::io::Result<()> {
    use std::io::ErrorKind;

    let mut f = try!(File::open(path));
    let mut buffer = String::new();
    let mut check = 0;
    if let Err(e) = f.read_to_string(&mut buffer) {
        match e.kind() {
            ErrorKind::InvalidData => check = check | HAS_ILLEGAL_CHARACTERS,
            _ => return Err(e),
        }
    }
    // only check content if we could read the file
    if check == 0 { check = try!(check_content(&buffer)); }
    if (check & HAS_ILLEGAL_CHARACTERS) > 0 {
        println!("HAS_ILLEGAL_CHARACTERS:[{}]", path.display());
    }
    if (check & HAS_TABS) > 0 {
        println!("HAS_TABS:[{}]", path.display());
    }
    if (check & TRAILING_SPACES) > 0 {
        println!("TRAILING_SPACES:[{}]", path.display());
        if clean {
            println!("cleaning trailing whitespaces");
            let res_string = try!(clean_string(&buffer));
            let mut file = try!(File::create(path));
            try!(file.write_all(res_string.as_bytes()));
        }
    }
    Ok(())
}

struct EnforcerCfg {
    unwanted: Vec<String>,
}
impl std::fmt::Debug for EnforcerCfg {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "[unwanted]: {:?}", self.unwanted)
    }
}

fn is_unwanted(path_elem: &str, unwanted_cfg: &EnforcerCfg) -> bool {
    unwanted_cfg.unwanted.iter()
        .any(|x| Pattern::new(x)
            .unwrap()
            .matches(path_elem))
}

fn load_config<'a>(input: &'a str) -> std::io::Result<EnforcerCfg> {
    use std::io::{Error, ErrorKind};

    let mut parser = toml::Parser::new(input);
    match parser.parse() {
        Some(toml) => {
            let toignore = toml["ignore"].as_slice().unwrap();
            let mut xs = Vec::new();
            for i in toignore {
                xs.push(i.as_str().unwrap().to_string());
            }
            return Ok(EnforcerCfg { unwanted: xs })
        },
        None => {
            return Err(Error::new(ErrorKind::Other, "oh no!"));
        }
    };
}

#[allow(dead_code)]
fn main() {
    use glob::glob;
    env_logger::init().unwrap();

    fn get_cfg() -> EnforcerCfg {
        fn read_unwanted() -> std::io::Result<EnforcerCfg> {
            let mut cfg_file = try!(File::open(".enforcer"));
            let mut buffer = String::new();
            try!(cfg_file.read_to_string(&mut buffer));
            load_config(&buffer[..])
        }
        let default_cfg = EnforcerCfg { unwanted: vec![".git".to_string(), ".bake".to_string()]};
        let unwanted_cfg = read_unwanted()
            .unwrap_or(default_cfg);
        println!("loaded ignores: {:?}", unwanted_cfg.unwanted);
        unwanted_cfg
    }

    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let pat = args.arg_glob.to_string();

    let unwanted_cfg = get_cfg();
    for path in glob(&*pat).unwrap()
        .filter_map(Result::ok)
        .filter(|x| !x.components().any(|y| is_unwanted(y.as_os_str().to_str().unwrap(), &unwanted_cfg))) {
            if !is_dir(path.as_path()) {
                check_path(path.as_path(), args.flag_clean)
                    .ok()
                    .expect(&format!("check_path for {:?} should work", path));
            }
    }
}

#[cfg(test)]
mod tests {
    use super::clean_string;
    use super::load_config;
    use super::is_unwanted;
    use glob::Pattern;
    use super::EnforcerCfg;

    #[test]
    fn test_clean_does_not_remove_trailing_newline() {
        let content = "1\n2\n3\n4\n5\n";
        let cleaned = clean_string(content).unwrap();
        assert!(cleaned.eq(content));
    }
    #[test]
    fn test_clean_trailing_whitespace() {
        let content = "1 \n2";
        let cleaned = clean_string(content).unwrap();
        println!("{:?}", cleaned);
        assert!(cleaned.eq("1\n2"));
    }
    #[test]
    fn test_load_simple_config() {
        println!("starting....");
        let c = include_str!("../samples/.enforcer");
        println!("starting2....{}", c);
        let cfg = load_config(c).unwrap();
        println!("{:?}", cfg.unwanted);
    }
    #[test]
    fn test_glob() {
        assert!(Pattern::new("build_*").unwrap().matches("build_Debug"));
    }
    #[test]
    fn test_is_unwanted() {
        let cfg = EnforcerCfg { unwanted: vec!["build_*".to_string(), ".git".to_string()]};
        assert!(is_unwanted("build_Debug", &cfg));
        assert!(is_unwanted(".git", &cfg));
        assert!(!is_unwanted("bla", &cfg));
    }
}

<|endoftext|>


<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    const SPALTEN:usize = 16;
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, TODO 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => if cursorpos != 0 {cursorpos-=1}, //left is "-1"
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => if cursorpos != buffer.len()-1 {cursorpos+=1}, //right is "+1"
                 48 => cursorpos -= cursorpos%16, //start of line is "to start"
                 36 => if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)} //dollar is "to end"
                        else {cursorpos = buffer.len()-1},
                114 => mode = 1, //r replaces the next char
                 58 => {mode = 2; // ":"
                            command.clear();},
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 { // r was pressed so replace next char
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8; mode = 0 },
                27 => mode = 0,
                _ => (),
            }
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); }, //TODO check this
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
            if z*spalten+s < buffer.len() { printw(&format!("{:02X} ", buffer[z*spalten+s]) ); }
                else {printw("-- "); }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
        //TODO: use (screenheight) maxzeilen to draw the command on last line of terminal
    }
    printw(&format!("{}", command));
}
<commit_msg>Add hexdino logo to docs<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/dbrgn/hexdino/master/logo.svg")]

use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    const SPALTEN:usize = 16;
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, TODO 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => if cursorpos != 0 {cursorpos-=1}, //left is "-1"
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => if cursorpos != buffer.len()-1 {cursorpos+=1}, //right is "+1"
                 48 => cursorpos -= cursorpos%16, //start of line is "to start"
                 36 => if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)} //dollar is "to end"
                        else {cursorpos = buffer.len()-1},
                114 => mode = 1, //r replaces the next char
                 58 => {mode = 2; // ":"
                            command.clear();},
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 { // r was pressed so replace next char
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8; mode = 0 },
                27 => mode = 0,
                _ => (),
            }
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); }, //TODO check this
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
            if z*spalten+s < buffer.len() { printw(&format!("{:02X} ", buffer[z*spalten+s]) ); }
                else {printw("-- "); }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
        //TODO: use (screenheight) maxzeilen to draw the command on last line of terminal
    }
    printw(&format!("{}", command));
}
<|endoftext|>


<commit_before>extern crate lancelot;
extern crate log;

use failure::{Error, Fail};
use log::{error, info, trace};
use std::env;
use std::process;

use lancelot::arch::*;
use lancelot::workspace::Workspace;

// TODO: removeme
use goblin::{Object};

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
}

pub struct Config {
    pub mode: u8,
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 3 {
            return Err("not enough arguments: provide `lancelot.exe 32|64 /path/to/input`");
        }

        let mode = match args[1].as_ref() {
            "32" => 32,
            "64" => 64,
            _ => return Err("invalid mode, pick one of `32` or `64`"),
        };

        let filename = args[2].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { mode, filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    if args.mode == 32 {
        let mut ws = Workspace::<Arch32>::from_file(&args.filename)?.load()?;
    } else if args.mode == 64 {
        let mut ws = Workspace::<Arch64>::from_file(&args.filename)?.load()?;

        if ws.loader.get_name() == "Windows/64/PE" {
            if let Ok(Object::PE(pe)) = Object::parse(&ws.buf) {
                let entry = pe.entry;
                let exports: Vec<usize> = pe.exports.iter()
                    // re-exports are simply strings that point to a `DLL.export_name` ASCII string.
                    // therefore, they're not functions/code.
                    .filter(|exp| exp.reexport.is_none())
                    .map(|exp| exp.rva).collect();

                info!("PE entry: {:#x}", entry);
                ws.make_insn(entry as i64)?;
                ws.analyze()?;
                for export in exports.iter() {
                    info!("export: {:#x}", export);
                    ws.make_insn(*export as i64)?;
                    ws.analyze()?;
                }
            }
        }
    }

    Ok(())
}

fn main() {
    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<commit_msg>main: add symbols for exports<commit_after>extern crate lancelot;
extern crate log;

use failure::{Error, Fail};
use log::{error, info, trace};
use std::env;
use std::process;

use lancelot::arch::*;
use lancelot::workspace::Workspace;

// TODO: removeme
use goblin::{Object};

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
}

pub struct Config {
    pub mode: u8,
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 3 {
            return Err("not enough arguments: provide `lancelot.exe 32|64 /path/to/input`");
        }

        let mode = match args[1].as_ref() {
            "32" => 32,
            "64" => 64,
            _ => return Err("invalid mode, pick one of `32` or `64`"),
        };

        let filename = args[2].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { mode, filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    if args.mode == 32 {
        let mut ws = Workspace::<Arch32>::from_file(&args.filename)?.load()?;
    } else if args.mode == 64 {
        let mut ws = Workspace::<Arch64>::from_file(&args.filename)?.load()?;

        if ws.loader.get_name() == "Windows/64/PE" {
            if let Ok(Object::PE(pe)) = Object::parse(&ws.buf) {
                let entry = pe.entry;
                let exports: Vec<(usize, Option<String>)> = pe.exports.iter()
                    // re-exports are simply strings that point to a `DLL.export_name` ASCII string.
                    // therefore, they're not functions/code.
                    .filter(|exp| exp.reexport.is_none())
                    .map(|exp| {
                        (exp.rva, exp.name.map(|n| n.to_string()))
                    }).collect();

                // TODO: need to add symbols for re-exports

                info!("PE entry: {:#x}", entry);
                ws.make_symbol(entry as i64, "entry");
                ws.make_insn(entry as i64)?;
                ws.analyze()?;
                for (rva, name) in exports.iter() {
                    info!("export: {:#x}", rva);
                    ws.make_insn(*rva as i64)?;
                    if let Some(name) = name {
                        ws.make_symbol(*rva as i64, &name)?;
                    }
                    ws.analyze()?;
                }
            }
        }
    }

    Ok(())
}

fn main() {
    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate reroute;

use std::fs::File;
use std::io::{BufReader, Result};
use std::io::prelude::*;
use std::collections::HashSet;
use std::env;

use hyper::Server;
use hyper::server::{Request, Response};
use reroute::{Captures, RouterBuilder};

fn not_found(req: Request, res: Response, _: Captures) {
    let uri = format!("{}", req.uri);
    let msg = format!("No service found behind '{}'.", uri);
    res.send(msg.as_bytes()).unwrap();
}

fn parse_capture(cap: &String) -> HashSet<String> {
    let caps: Vec<&str> = cap.split(|c| c == ',' || c == '/').collect();
    // let mut list: Vec<String> = Vec::new();
    let mut list: HashSet<String> = HashSet::new();
    // &caps[2..] is used to skip `/api/`
    for val in &caps[2..] {
        if val.is_empty() {
            continue;
        }
        list.insert(val.to_string());
    }
    list
}

fn list_handler(_: Request, res: Response, c: Captures) {
    let cap = c.unwrap();
    let caps = parse_capture(&cap[0]);
    let mut re = "; Generated by editorconfig.me\n; EditorConfig helps developers define and maintain consistent\n; coding styles between different editors and IDEs\n; editorconfig.org\n\n".to_string();

    for target in caps {
        let content = match read_file(&get_file_name(&target)) {
            Ok(c) => c,
            Err(e) => {
                println!("Could not process '{}'. {:?}", target, e);
                format!("; Could not process '{}'\n", target)
            }
        };
        re.push_str(&content);
        re.push_str("\n");
    }
    res.send(re.as_bytes()).unwrap();
}

fn get_file_name(name: &String) -> String {
    let mut full_name = "./files/".to_string();
    full_name.push_str(&name);
    full_name.push_str(".editorconfig");
    full_name
}

fn read_file(name: &String) -> Result<String> {
    // let mut full_name = "./files/".to_string();
    // full_name.push_str(&name);
    let file = File::open(name)?;
    let mut reader = BufReader::new(file);
    let mut contents = String::new();

    reader.read_to_string(&mut contents)?;

    Ok(contents)
}

// Look up the server port in PORT for heroku or use 8080 as fallback
fn get_server_port() -> String {
    env::var("PORT").unwrap_or("8080".to_string())
}

fn main() {
    let mut builder = RouterBuilder::new();

    builder.get(r"/api/(,?(\w)*)+", list_handler);

    builder.not_found(not_found);

    let router = builder.finalize().unwrap();

    let mut listen = "0.0.0.0:".to_string();
    listen.push_str(&get_server_port());
    Server::http(&listen).unwrap().handle(router).unwrap();
}

#[test]
fn test_parse_capture() {
    let actual = parse_capture(&"/api/,rust,,markdown,,,rust,".to_string());
    let expected: HashSet<String> = vec!["rust".to_string(), "markdown".to_string()]
        .into_iter()
        .collect();
    assert_eq!(actual, expected);
}

#[test]
fn test_read_file() {
    let actual = read_file(&"test/testfile".to_string()).unwrap();
    let expected = "testtesttest\n".to_string();
    assert_eq!(actual, expected);
}

#[test]
fn test_get_file_name() {
    let actual = get_file_name(&"rust".to_string());
    let expected = "./files/rust.editorconfig".to_string();
    assert_eq!(actual, expected);
}
<commit_msg>Cleanup<commit_after>extern crate hyper;
extern crate reroute;

use std::fs::File;
use std::io::{BufReader, Result};
use std::io::prelude::*;
use std::collections::HashSet;
use std::env;

use hyper::Server;
use hyper::server::{Request, Response};
use reroute::{Captures, RouterBuilder};

fn not_found(req: Request, res: Response, _: Captures) {
    let uri = format!("{}", req.uri);
    let msg = format!("No service found behind '{}'.", uri);
    res.send(msg.as_bytes()).unwrap();
}

fn parse_capture(cap: &String) -> HashSet<String> {
    let caps: Vec<&str> = cap.split(|c| c == ',' || c == '/').collect();
    let mut set: HashSet<String> = HashSet::new();
    // &caps[2..] is used to skip `/api/`
    for val in &caps[2..] {
        if val.is_empty() {
            continue;
        }
        set.insert(val.to_string());
    }
    set
}

fn list_handler(_: Request, res: Response, c: Captures) {
    let cap = c.unwrap();
    let caps = parse_capture(&cap[0]);
    let mut re = "; Generated by editorconfig.me\n; EditorConfig helps developers define and maintain consistent\n; coding styles between different editors and IDEs\n; editorconfig.org\n\n".to_string();

    for target in caps {
        let content = match read_file(&get_file_name(&target)) {
            Ok(c) => c,
            Err(e) => {
                println!("Could not process '{}'. {:?}", target, e);
                format!("; Could not process '{}'\n", target)
            }
        };
        re.push_str(&content);
        re.push_str("\n");
    }
    res.send(re.as_bytes()).unwrap();
}

fn get_file_name(name: &String) -> String {
    let mut full_name = "./files/".to_string();
    full_name.push_str(&name);
    full_name.push_str(".editorconfig");
    full_name
}

fn read_file(name: &String) -> Result<String> {
    let file = File::open(name)?;
    let mut reader = BufReader::new(file);
    let mut contents = String::new();

    reader.read_to_string(&mut contents)?;

    Ok(contents)
}

/// Look up the server port in PORT for heroku or use 8080 as fallback
fn get_server_port() -> String {
    env::var("PORT").unwrap_or("8080".to_string())
}

fn main() {
    let mut builder = RouterBuilder::new();

    builder.get(r"/api/(,?(\w)*)+", list_handler);

    builder.not_found(not_found);

    let router = builder.finalize().unwrap();

    let mut listen = "0.0.0.0:".to_string();
    listen.push_str(&get_server_port());
    Server::http(&listen).unwrap().handle(router).unwrap();
}

#[test]
fn test_parse_capture() {
    let actual = parse_capture(&"/api/,rust,,markdown,,,rust,".to_string());
    let expected: HashSet<String> = vec!["rust".to_string(), "markdown".to_string()]
        .into_iter()
        .collect();
    assert_eq!(actual, expected);
}

#[test]
fn test_read_file() {
    let actual = read_file(&"test/testfile".to_string()).unwrap();
    let expected = "testtesttest\n".to_string();
    assert_eq!(actual, expected);
}

#[test]
fn test_get_file_name() {
    let actual = get_file_name(&"rust".to_string());
    let expected = "./files/rust.editorconfig".to_string();
    assert_eq!(actual, expected);
}
<|endoftext|>


<commit_before>extern crate num_cpus;

use std::env;
use std::io::{self, Write, StderrLock};
use std::process::{Command, exit};
use std::thread::{self, JoinHandle};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

/* TODO: Functionality can be increased to accept the following syntaxes from GNU Parallel:
 - Stdin support is currently missing.
 - {N}, {N.}, etc.
 - parallel command {1} {2} {3} ::: 1 2 3 ::: 4 5 6 ::: 7 8 9
 - paralllel command ::: a b c :::+ 1 2 3 ::: d e f :::+ 4 5 6
*/

fn main() {
    let stderr = io::stderr();
    let mut ncores = num_cpus::get();
    let mut command = String::new();
    let mut arg_tokens = Vec::new();
    let mut inputs = Vec::new();

    // Let's collect all parameters that we need from the program's arguments.
    // If an error is returned, this will handle that error as efficiently as possible.
    if let Err(why) = parse_arguments(&mut ncores, &mut command, &mut arg_tokens, &mut inputs) {
        let mut stderr = stderr.lock();
        let _ = stderr.write(b"parallel: parsing error: ");
        match why {
            ParseErr::JobsNaN(value) => {
                let _ = stderr.write(b"jobs parameter, '");
                let _ = stderr.write(value.as_bytes());
                let _ = stderr.write(b"', is not a number.\n");
            },
            _ => {
                let message: &[u8] = match why {
                    ParseErr::InputVarsNotDefined => b"input variables were not defined.\n",
                    ParseErr::JobsNoValue         => b"no jobs parameter was defined.\n",
                    _ => unreachable!()
                };
                let _ = stderr.write(message);
            }
        };
        exit(1);
    }

    // If no command was given, then the inputs are actually commands themselves.
    let input_is_command = command.is_empty();

    // It will be useful to know the number of inputs, to know when to quit.
    let num_inputs = inputs.len();

    // Stores the next input to be processed
    let shared_counter = Arc::new(AtomicUsize::new(0));

    // We will share the same list of inputs with each thread.
    let shared_input = Arc::new(inputs);

    // First we will create as many threads as `ncores` specifies.
    // The `threads` vector will contain the thread handles needed to
    // know when to quit the program.
    let mut threads: Vec<JoinHandle<()>> = Vec::with_capacity(ncores);
    for slot in 1..ncores+1 {
        // The command that each input variable will be sent to.
        let command = command.clone();
        // The arguments for the command.
        let argument_tokens = arg_tokens.clone();
        // Allow the thread to gain access to the list of inputs.
        let input = shared_input.clone();
        // Allow the thread to access the current command counter
        let counter = shared_counter.clone();
        // Allow the thread to know when it's time to stop.
        let num_inputs = num_inputs;

        // The actual thread where the work will happen on incoming data.
        let handle: JoinHandle<()> = thread::spawn(move || {
            let slot_number = slot;
            let stderr = io::stderr();
            loop {
                // Obtain the Nth item and it's job ID from the list of inputs.
                let (input_var, job_id) = {
                    // Atomically increment the counter
                    let old_counter = counter.fetch_add(1, Ordering::SeqCst);
                    if old_counter >= num_inputs {
                        break
                    } else {
                        let input_var = &input[old_counter];
                        let job_id = old_counter + 1;
                        (input_var, job_id)
                    }
                };

                if input_is_command {
                    // The inputs are actually the commands.
                    let mut iterator = input_var.split_whitespace();
                    let actual_command = iterator.next().unwrap();
                    let args = iterator.collect::<Vec<&str>>();
                    if let Err(_) = Command::new(actual_command).args(&args).status() {
                        let mut stderr = stderr.lock();
                        let _ = stderr.write(b"parallel: command error: ");
                        let _ = stderr.write(input_var.as_bytes());
                        let _ = stderr.write(b"\n");
                    }
                } else {
                    // Build a command by merging the command template with the input,
                    // and then execute that command.
                    let (slot, job) = (slot_number.to_string(), job_id.to_string());
                    if let Err(cmd_err) = cmd_builder(input_var, &command, &argument_tokens,
                        &slot, &job)
                    {
                        let mut stderr = stderr.lock();
                        cmd_err.handle(&mut stderr);
                    }
                }
            }
        });

        // After the thread has been created, add the important pieces needed by the
        // main thread to the `threads` vector.
        threads.push(handle);
    }

    for thread in threads.into_iter() { thread.join().unwrap(); }
}

enum CommandErr {
    Failed(String, Vec<String>)
}

impl CommandErr {
    fn handle(self, stderr: &mut StderrLock) {
        let _ = stderr.write(b"parallel: command error: ");
        match self {
            CommandErr::Failed(command, arguments) => {
                let _ = stderr.write(command.as_bytes());
                for arg in &arguments {
                    let _ = stderr.write(b" ");
                    let _ = stderr.write(arg.as_bytes());
                }
                let _ = stderr.write(b"\n");
            }
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
enum Token {
    Character(char),
    Placeholder,
    RemoveExtension,
    Basename,
    Dirname,
    BaseAndExt,
    Slot,
    Job
}

fn tokenize(template: &str) -> Vec<Token> {
    let mut matching = false;
    let mut tokens = Vec::new();
    let mut pattern = String::new();
    for character in template.chars() {
        match (character, matching) {
            ('{', false) => matching = true,
            ('}', true)  => {
                matching = false;
                if pattern.is_empty() {
                    tokens.push(Token::Placeholder);
                } else {
                    match match_token(&pattern) {
                        Some(token) => tokens.push(token),
                        None => {
                            tokens.push(Token::Character('{'));
                            for character in pattern.chars() {
                                tokens.push(Token::Character(character));
                            }
                            tokens.push(Token::Character('}'));
                        }
                    }
                    pattern.clear();
                }
            }
            (_, false)  => tokens.push(Token::Character(character)),
            (_, true) => pattern.push(character)
        }
    }
    tokens
}

fn match_token(pattern: &str) -> Option<Token> {
    match pattern {
        "."  => Some(Token::RemoveExtension),
        "#"  => Some(Token::Job),
        "%"  => Some(Token::Slot),
        "/"  => Some(Token::Basename),
        "//" => Some(Token::Dirname),
        "/." => Some(Token::BaseAndExt),
        _    => None
    }

}

/// Builds the command and executes it
fn cmd_builder(input: &str, command: &str, arg_tokens: &[Token], slot_id: &str, job_id: &str)
    -> Result<(), CommandErr>
{
    let mut arguments = String::new();
    for arg in arg_tokens {
        match *arg {
            Token::Character(arg) => arguments.push(arg),
            Token::Basename => arguments.push_str(basename(input)),
            Token::BaseAndExt => arguments.push_str(basename(remove_extension(input))),
            Token::Dirname => arguments.push_str(dirname(input)),
            Token::Job => arguments.push_str(job_id),
            Token::Placeholder => arguments.push_str(input),
            Token::RemoveExtension => arguments.push_str(remove_extension(input)),
            Token::Slot => arguments.push_str(slot_id)
        }
    }

    let placeholder_exists = arg_tokens.iter().any(|ref x| {
        x == &&Token::BaseAndExt || x == &&Token::Basename || x == &&Token::Dirname ||
        x == &&Token::Job || x == &&Token::Placeholder || x == &&Token::RemoveExtension ||
        x == &&Token::Slot
    });

    if !placeholder_exists {
        arguments.push_str(input);
    }

    let arguments = arguments.split_whitespace().map(|x| x.to_owned()).collect::<Vec<String>>();

    if let Err(_) = Command::new(&command).args(&arguments).status() {
        return Err(CommandErr::Failed(String::from(command), arguments));
    }
    Ok(())
}

/// Removes the extension of a given input
fn remove_extension(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '.' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

fn basename(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { "." } else { &input[0..index] }
}

enum ParseErr {
    JobsNaN(String),
    JobsNoValue,
    InputVarsNotDefined,
}

// Parses input arguments and stores their values into their associated variabless.
fn parse_arguments(ncores: &mut usize, command: &mut String, arg_tokens: &mut Vec<Token>,
    input_variables: &mut Vec<String>) -> Result<(), ParseErr>
{
    let mut parsing_arguments = true;
    let mut command_is_set    = false;
    let mut raw_args = env::args().skip(1).peekable();
    let mut comm = String::new();
    while let Some(argument) = raw_args.next() {
        if parsing_arguments {
            match argument.as_str() {
                // Defines the number of jobs to run in parallel.
                "-j"  => {
                    match raw_args.peek() {
                        Some(val) => match val.parse::<usize>() {
                            Ok(val) => *ncores = val,
                            Err(_)  => return Err(ParseErr::JobsNaN(val.clone()))
                        },
                        None => return Err(ParseErr::JobsNoValue)
                    }
                    let _ = raw_args.next();
                },
                // Arguments after `:::` are input values.
                ":::" => parsing_arguments = false,
                _ => {
                    if command_is_set {
                        comm.push(' ');
                        comm.push_str(&argument);
                    } else {
                        comm.push_str(&argument);
                        command_is_set = true;
                    }

                }
            }
        } else {
            input_variables.push(argument);
        }
    }

    // This will fill in command and argument information needed by the threads.
    // If there is a space in the argument, then the command has arguments
    match comm.chars().position(|x| x == ' ') {
        Some(pos) => {
            *command    = String::from(&comm[0..pos]);
            *arg_tokens = tokenize(&comm[pos+1..]);
        },
        None => *command = comm
    }

    if input_variables.is_empty() { return Err(ParseErr::InputVarsNotDefined) }
    Ok(())
}

#[test]
fn tokenizer_character() {
    assert_eq!(tokenize("foo"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o')]);
}

#[test]
fn tokenizer_placeholder() {
    assert_eq!(tokenize("{}"), vec![Token::Placeholder]);
}

#[test]
fn tokenizer_remove_extension() {
    assert_eq!(tokenize("{.}"), vec![Token::RemoveExtension]);
}

#[test]
fn tokenizer_basename() {
    assert_eq!(tokenize("{/}"), vec![Token::Basename]);
}

#[test]
fn tokenizer_dirname() {
    assert_eq!(tokenize("{//}"), vec![Token::Dirname]);
}

#[test]
fn tokenizer_base_and_ext() {
    assert_eq!(tokenize("{/.}"), vec![Token::BaseAndExt]);
}

#[test]
fn tokenizer_slot() {
    assert_eq!(tokenize("{%}"), vec![Token::Slot]);
}

#[test]
fn tokenizer_job() {
    assert_eq!(tokenize("{#}"), vec![Token::Job]);
}

#[test]
fn tokenizer_multiple() {
    assert_eq!(tokenize("foo {} bar"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o'), Token::Character(' '), Token::Placeholder, Token::Character(' '), Token::Character('b'), Token::Character('a'), Token::Character('r')]);
}

#[test]
fn tokenizer_no_space() {
    assert_eq!(tokenize("foo{}bar"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o'), Token::Placeholder, Token::Character('b'), Token::Character('a'), Token::Character('r')]);
}

#[test]
fn path_remove_ext_simple() {
    assert_eq!(remove_extension("foo.txt"), "foo");
}

#[test]
fn path_remove_ext_dir() {
    assert_eq!(remove_extension("dir/foo.txt"), "dir/foo");
}

#[test]
fn path_remove_ext_empty() {
    assert_eq!(remove_extension(""), "");
}

#[test]
fn path_basename_simple() {
    assert_eq!(basename("foo.txt"), "foo.txt");
}

#[test]
fn path_basename_dir() {
    assert_eq!(basename("dir/foo.txt"), "foo.txt");
}

#[test]
fn path_basename_empty() {
    assert_eq!(basename(""), "");
}

#[test]
fn path_dirname_simple() {
    assert_eq!(dirname("foo.txt"), ".");
}

#[test]
fn path_dirname_dir() {
    assert_eq!(dirname("dir/foo.txt"), "dir");
}

#[test]
fn path_dirname_empty() {
    assert_eq!(dirname(""), ".");
}
<commit_msg>Refactor Argument Building This allows for an additional test to be created.<commit_after>extern crate num_cpus;

use std::env;
use std::io::{self, Write, StderrLock};
use std::process::{Command, exit};
use std::thread::{self, JoinHandle};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

/* TODO: Functionality can be increased to accept the following syntaxes from GNU Parallel:
 - Stdin support is currently missing.
 - {N}, {N.}, etc.
 - parallel command {1} {2} {3} ::: 1 2 3 ::: 4 5 6 ::: 7 8 9
 - paralllel command ::: a b c :::+ 1 2 3 ::: d e f :::+ 4 5 6
*/

fn main() {
    let stderr = io::stderr();
    let mut ncores = num_cpus::get();
    let mut command = String::new();
    let mut arg_tokens = Vec::new();
    let mut inputs = Vec::new();

    // Let's collect all parameters that we need from the program's arguments.
    // If an error is returned, this will handle that error as efficiently as possible.
    if let Err(why) = parse_arguments(&mut ncores, &mut command, &mut arg_tokens, &mut inputs) {
        let mut stderr = stderr.lock();
        let _ = stderr.write(b"parallel: parsing error: ");
        match why {
            ParseErr::JobsNaN(value) => {
                let _ = stderr.write(b"jobs parameter, '");
                let _ = stderr.write(value.as_bytes());
                let _ = stderr.write(b"', is not a number.\n");
            },
            _ => {
                let message: &[u8] = match why {
                    ParseErr::InputVarsNotDefined => b"input variables were not defined.\n",
                    ParseErr::JobsNoValue         => b"no jobs parameter was defined.\n",
                    _ => unreachable!()
                };
                let _ = stderr.write(message);
            }
        };
        exit(1);
    }

    // If no command was given, then the inputs are actually commands themselves.
    let input_is_command = command.is_empty();

    // It will be useful to know the number of inputs, to know when to quit.
    let num_inputs = inputs.len();

    // Stores the next input to be processed
    let shared_counter = Arc::new(AtomicUsize::new(0));

    // We will share the same list of inputs with each thread.
    let shared_input = Arc::new(inputs);

    // First we will create as many threads as `ncores` specifies.
    // The `threads` vector will contain the thread handles needed to
    // know when to quit the program.
    let mut threads: Vec<JoinHandle<()>> = Vec::with_capacity(ncores);
    for slot in 1..ncores+1 {
        // The command that each input variable will be sent to.
        let command = command.clone();
        // The arguments for the command.
        let argument_tokens = arg_tokens.clone();
        // Allow the thread to gain access to the list of inputs.
        let input = shared_input.clone();
        // Allow the thread to access the current command counter
        let counter = shared_counter.clone();
        // Allow the thread to know when it's time to stop.
        let num_inputs = num_inputs;

        // The actual thread where the work will happen on incoming data.
        let handle: JoinHandle<()> = thread::spawn(move || {
            let slot_number = slot;
            let stderr = io::stderr();
            loop {
                // Obtain the Nth item and it's job ID from the list of inputs.
                let (input_var, job_id) = {
                    // Atomically increment the counter
                    let old_counter = counter.fetch_add(1, Ordering::SeqCst);
                    if old_counter >= num_inputs {
                        break
                    } else {
                        let input_var = &input[old_counter];
                        let job_id = old_counter + 1;
                        (input_var, job_id)
                    }
                };

                if input_is_command {
                    // The inputs are actually the commands.
                    let mut iterator = input_var.split_whitespace();
                    let actual_command = iterator.next().unwrap();
                    let args = iterator.collect::<Vec<&str>>();
                    if let Err(_) = Command::new(actual_command).args(&args).status() {
                        let mut stderr = stderr.lock();
                        let _ = stderr.write(b"parallel: command error: ");
                        let _ = stderr.write(input_var.as_bytes());
                        let _ = stderr.write(b"\n");
                    }
                } else {
                    // Build a command by merging the command template with the input,
                    // and then execute that command.
                    let (slot, job) = (slot_number.to_string(), job_id.to_string());
                    if let Err(cmd_err) = cmd_builder(input_var, &command, &argument_tokens,
                        &slot, &job)
                    {
                        let mut stderr = stderr.lock();
                        cmd_err.handle(&mut stderr);
                    }
                }
            }
        });

        // After the thread has been created, add the important pieces needed by the
        // main thread to the `threads` vector.
        threads.push(handle);
    }

    for thread in threads.into_iter() { thread.join().unwrap(); }
}

enum CommandErr {
    Failed(String, Vec<String>)
}

impl CommandErr {
    fn handle(self, stderr: &mut StderrLock) {
        let _ = stderr.write(b"parallel: command error: ");
        match self {
            CommandErr::Failed(command, arguments) => {
                let _ = stderr.write(command.as_bytes());
                for arg in &arguments {
                    let _ = stderr.write(b" ");
                    let _ = stderr.write(arg.as_bytes());
                }
                let _ = stderr.write(b"\n");
            }
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
enum Token {
    Character(char),
    Placeholder,
    RemoveExtension,
    Basename,
    Dirname,
    BaseAndExt,
    Slot,
    Job
}

fn tokenize(template: &str) -> Vec<Token> {
    let mut matching = false;
    let mut tokens = Vec::new();
    let mut pattern = String::new();
    for character in template.chars() {
        match (character, matching) {
            ('{', false) => matching = true,
            ('}', true)  => {
                matching = false;
                if pattern.is_empty() {
                    tokens.push(Token::Placeholder);
                } else {
                    match match_token(&pattern) {
                        Some(token) => tokens.push(token),
                        None => {
                            tokens.push(Token::Character('{'));
                            for character in pattern.chars() {
                                tokens.push(Token::Character(character));
                            }
                            tokens.push(Token::Character('}'));
                        }
                    }
                    pattern.clear();
                }
            }
            (_, false)  => tokens.push(Token::Character(character)),
            (_, true) => pattern.push(character)
        }
    }
    tokens
}

fn match_token(pattern: &str) -> Option<Token> {
    match pattern {
        "."  => Some(Token::RemoveExtension),
        "#"  => Some(Token::Job),
        "%"  => Some(Token::Slot),
        "/"  => Some(Token::Basename),
        "//" => Some(Token::Dirname),
        "/." => Some(Token::BaseAndExt),
        _    => None
    }

}

/// Builds the command and executes it
fn cmd_builder(input: &str, command: &str, arg_tokens: &[Token], slot_id: &str, job_id: &str)
    -> Result<(), CommandErr>
{
    // First the arguments will be generated based on the tokens and input.
    let mut arguments = Vec::new();
    build_arguments(&mut arguments, arg_tokens, input, slot_id, job_id);

    // Check to see if any placeholder tokens are in use.
    let placeholder_exists = arg_tokens.iter().any(|ref x| {
        x == &&Token::BaseAndExt || x == &&Token::Basename || x == &&Token::Dirname ||
        x == &&Token::Job || x == &&Token::Placeholder || x == &&Token::RemoveExtension ||
        x == &&Token::Slot
    });

    // If no placeholder tokens are in use, the user probably wants to infer one.
    if !placeholder_exists {
        arguments.push(String::from(input));
    }

    // Attempt to execute the command with the generated arguments.
    if let Err(_) = Command::new(&command).args(&arguments).status() {
        // If an error status is returned, return it to be printed.
        return Err(CommandErr::Failed(String::from(command), arguments));
    }
    Ok(())
}

/// Builds arguments using the `tokens` template with the current `input` value.
/// The arguments will be stored within a `Vec<String>`
fn build_arguments(args_vec: &mut Vec<String>, tokens: &[Token], input: &str, slot: &str,
    job: &str)
{
    let mut arguments = String::new();
    for arg in tokens {
        match *arg {
            Token::Character(arg)  => arguments.push(arg),
            Token::Basename        => arguments.push_str(basename(input)),
            Token::BaseAndExt      => arguments.push_str(basename(remove_extension(input))),
            Token::Dirname         => arguments.push_str(dirname(input)),
            Token::Job             => arguments.push_str(job),
            Token::Placeholder     => arguments.push_str(input),
            Token::RemoveExtension => arguments.push_str(remove_extension(input)),
            Token::Slot            => arguments.push_str(slot)
        }
    }

    for argument in arguments.split_whitespace() {
        args_vec.push(String::from(argument));
    }
}

/// Removes the extension of a given input
fn remove_extension(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '.' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

fn basename(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { "." } else { &input[0..index] }
}

enum ParseErr {
    JobsNaN(String),
    JobsNoValue,
    InputVarsNotDefined,
}

// Parses input arguments and stores their values into their associated variabless.
fn parse_arguments(ncores: &mut usize, command: &mut String, arg_tokens: &mut Vec<Token>,
    input_variables: &mut Vec<String>) -> Result<(), ParseErr>
{
    let mut parsing_arguments = true;
    let mut command_is_set    = false;
    let mut raw_args = env::args().skip(1).peekable();
    let mut comm = String::new();
    while let Some(argument) = raw_args.next() {
        if parsing_arguments {
            match argument.as_str() {
                // Defines the number of jobs to run in parallel.
                "-j"  => {
                    match raw_args.peek() {
                        Some(val) => match val.parse::<usize>() {
                            Ok(val) => *ncores = val,
                            Err(_)  => return Err(ParseErr::JobsNaN(val.clone()))
                        },
                        None => return Err(ParseErr::JobsNoValue)
                    }
                    let _ = raw_args.next();
                },
                // Arguments after `:::` are input values.
                ":::" => parsing_arguments = false,
                _ => {
                    if command_is_set {
                        comm.push(' ');
                        comm.push_str(&argument);
                    } else {
                        comm.push_str(&argument);
                        command_is_set = true;
                    }

                }
            }
        } else {
            input_variables.push(argument);
        }
    }

    // This will fill in command and argument information needed by the threads.
    // If there is a space in the argument, then the command has arguments
    match comm.chars().position(|x| x == ' ') {
        Some(pos) => {
            *command    = String::from(&comm[0..pos]);
            *arg_tokens = tokenize(&comm[pos+1..]);
        },
        None => *command = comm
    }

    if input_variables.is_empty() { return Err(ParseErr::InputVarsNotDefined) }
    Ok(())
}

#[test]
fn tokenizer_character() {
    assert_eq!(tokenize("foo"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o')]);
}

#[test]
fn tokenizer_placeholder() {
    assert_eq!(tokenize("{}"), vec![Token::Placeholder]);
}

#[test]
fn tokenizer_remove_extension() {
    assert_eq!(tokenize("{.}"), vec![Token::RemoveExtension]);
}

#[test]
fn tokenizer_basename() {
    assert_eq!(tokenize("{/}"), vec![Token::Basename]);
}

#[test]
fn tokenizer_dirname() {
    assert_eq!(tokenize("{//}"), vec![Token::Dirname]);
}

#[test]
fn tokenizer_base_and_ext() {
    assert_eq!(tokenize("{/.}"), vec![Token::BaseAndExt]);
}

#[test]
fn tokenizer_slot() {
    assert_eq!(tokenize("{%}"), vec![Token::Slot]);
}

#[test]
fn tokenizer_job() {
    assert_eq!(tokenize("{#}"), vec![Token::Job]);
}

#[test]
fn tokenizer_multiple() {
    assert_eq!(tokenize("foo {} bar"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o'), Token::Character(' '), Token::Placeholder, Token::Character(' '), Token::Character('b'), Token::Character('a'), Token::Character('r')]);
}

#[test]
fn tokenizer_no_space() {
    assert_eq!(tokenize("foo{}bar"), vec![Token::Character('f'), Token::Character('o'), Token::Character('o'), Token::Placeholder, Token::Character('b'), Token::Character('a'), Token::Character('r')]);
}

#[test]
fn path_remove_ext_simple() {
    assert_eq!(remove_extension("foo.txt"), "foo");
}

#[test]
fn path_remove_ext_dir() {
    assert_eq!(remove_extension("dir/foo.txt"), "dir/foo");
}

#[test]
fn path_remove_ext_empty() {
    assert_eq!(remove_extension(""), "");
}

#[test]
fn path_basename_simple() {
    assert_eq!(basename("foo.txt"), "foo.txt");
}

#[test]
fn path_basename_dir() {
    assert_eq!(basename("dir/foo.txt"), "foo.txt");
}

#[test]
fn path_basename_empty() {
    assert_eq!(basename(""), "");
}

#[test]
fn path_dirname_simple() {
    assert_eq!(dirname("foo.txt"), ".");
}

#[test]
fn path_dirname_dir() {
    assert_eq!(dirname("dir/foo.txt"), "dir");
}

#[test]
fn path_dirname_empty() {
    assert_eq!(dirname(""), ".");
}

#[test]
fn build_arguments_test() {
    let input = "applesauce.mp4";
    let job   = "1";
    let slot  = "1";
    let tokens = vec![
        Token::Character('-'), Token::Character('i'), Token::Character(' '), Token::Placeholder,
        Token::Character(' '), Token::RemoveExtension, Token::Character('.'), Token::Character('m'),
        Token::Character('k'),Token::Character('v')
    ];
    let mut arguments = Vec::new();
    build_arguments(&mut arguments, &tokens, input, slot, job);
    let expected = vec![
        String::from("-i"), String::from("applesauce.mp4"), String::from("applesauce.mkv")
    ];
    assert_eq!(arguments, expected)
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate router;
extern crate mount;
extern crate staticfile;
extern crate robots;

use iron::prelude::*;
use iron::status;
use router::Router;
use staticfile::Static;
use mount::Mount;
use std::path::Path;

fn main() {
	// the router (for RESTfull actions)
	let mut router = Router::new();

	router.get("/wings", move_wings,"wings");
	router.get("/mouth", open_mouth,"mouth");
	router.get("/eyes", blink_eyes,"eyes");

	fn move_wings(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Wings moved!")))
	}

	fn open_mouth(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Mouth opened!")))
	}

	fn blink_eyes(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Eyes blinked!")))
	}

	// the mounter for static files
	let mut mount = Mount::new();
	mount
		.mount("/",Static::new(Path::new("static")))
		.mount("/api/",router);
	Iron::new(mount).http("0.0.0.0:3000").unwrap();
}
<commit_msg>actor system<commit_after>extern crate iron;
extern crate router;
extern crate mount;
extern crate staticfile;
extern crate robots;

use iron::prelude::*;
use iron::status;
use router::Router;
use staticfile::Static;
use mount::Mount;
use std::path::Path;
use std::any::Any;
use robots::actors::{ActorSystem,Actor,ActorCell,ActorContext,Props};

fn main() {
	// the router (for RESTfull actions)
	let mut router = Router::new();

	router.get("/wings", move_wings,"wings");
	router.get("/mouth", open_mouth,"mouth");
	router.get("/eyes", blink_eyes,"eyes");

	fn move_wings(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Wings moved!")))
	}

	fn open_mouth(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Mouth opened!")))
	}

	fn blink_eyes(_: &mut Request) -> IronResult<Response> {
		Ok(Response::with((status::Ok, "Eyes blinked!")))
	}

	let dragon_actor_system = ActorSystem::new("dragon".to_owned());
	dragon_actor_system.spawn_threads(3);
	// the mounter for static files
	let mut mount = Mount::new();
	mount
		.mount("/",Static::new(Path::new("static")))
		.mount("/api/",router);
	Iron::new(mount).http("0.0.0.0:3000").unwrap();
}

#[derive(Copy, Clone, PartialEq)]
enum DragonCommands {
	MoveWings,
	OpenMouth,
	BlinkEyes,
}

struct Dragon;

impl Actor for Dragon {
	fn receive(&self, _message: Box<Any>, _context: ActorCell){
		if let Ok(_message) = Box::<Any>::downcast::<DragonCommands>(_message){
			if *_message == DragonCommands::MoveWings {
				println!("Moving Wings");
			}
		}
	}
}

impl Dragon {
	fn new(_: ()) -> Dragon {
		Dragon
	}
}

struct Wings;

impl Actor for Wings {
    fn receive(&self, _message: Box<Any>, _context: ActorCell) {}
}

impl Wings {
    fn new(_: ()) -> Wings {
        Wings
    }
}
<|endoftext|>