<commit_before>extern crate delta_l as dl;
extern crate getopts;

use dl::DeltaL;
use dl::Mode::{Encrypt, Decrypt};
use dl::DeltaLError::{Io, InvalidHeader, ChecksumMismatch};

use std::env;
use std::string::String;

use std::io::ErrorKind::NotFound;

use getopts::Options;

fn main() {
    let args = &env::args().collect::<Vec<String>>()[1..];

    let mut opts = Options::new();
    opts.optflag("?", "help", "Prints this help menu.");
    opts.optopt("p", "", "Encrypts/decrypts with a passphrase.", "<passphrase>");
    opts.optopt("o", "", "Sets the output file.", "<output-file>");
    opts.optflag("y", "yes", "Overwrites output file without prompt, if it already exists.");
    opts.optflag("c", "checksum", "Disables checksum feature when encrypting: - This is read from the header when decrypting.");
    opts.optflag("f", "force", "Forces the resulting file to be created even if the checksums mismatch during decryption.");

    let matches = match opts.parse(args){
        Ok(m) => m,
        Err(_) => return incorrect_syntax(),
    };

    if matches.opt_present("?"){
        return print!("{}", opts.usage(USAGE));
    }

    if matches.free.len() != 2{
        return incorrect_syntax();
    }

    let file_path = &*matches.free[1];

    let mut dl = DeltaL::new(match &*matches.free[0]{
        "e"|"encrypt" => Encrypt{checksum: true},
        "d"|"decrypt" => Decrypt,
        _ => return incorrect_syntax()
    });

    let to_file = matches.opt_str("o");
    let passphrase = matches.opt_str("p");
    let checksum = matches.opt_present("c");
    let force = matches.opt_present("f");
    let force_overwite = matches.opt_present("y");

    if checksum {
        if dl.is_mode_encrypt(){
            dl.set_checksum(false);
        }else{
            return incorrect_syntax()
        }
    }

    if let Some(pp) = passphrase{
        dl.set_passphrase(&pp);
    }

    let to: PathBuf = to_file
        .unwrap_or(file_path.to_owned() + dl.get_mode_standard_extension())
        // From `String` into `PathBuf`
        .into();

    if to.exists() && !force_overwite{
        println!("Output file already exists; do you want to overwrite (yes/no)?");

        let stdin = std::io::stdin();

        loop{
            let mut answer = String::new();
            stdin.read_line(&mut answer).unwrap();

            match answer.trim().as_ref(){
                "yes" => break,
                "no"  => return println!("{}cryption has been cancelled.", if dl.is_mode_encrypt() {"En"} else {"De"}),
                _ => println!("Please answer yes or no:"),
            }
        }
    }

    match dl.execute(file_path) {
        Ok(res_vec) => {
            save(res_vec, &to).unwrap();
            println!("Result file has been saved to {}", to.to_str().unwrap_or("<nil>"))
        },
        Err(e) => match e {
            Io(e) => match e.kind(){
                NotFound     => println!("Couldn't find the specified file.\nPlease make sure the file exists."),
                _            => println!("An unknown error occured, encrypting the file:\n{:?}", e)
            },
            InvalidHeader    => println!("Invalid header error:\nThe specified file wasn't a valid .delta file."),
            ChecksumMismatch(res_vec) => if force{
                    println!("Checksum mismatch detetected! Saving anyways because of force flag");
                    save(res_vec, &to).unwrap();
                }else{
                    println!("Decryption failed:\nIncorrect passphrase.")
                }
        },
    }
}

use std::path::PathBuf;
use std::fs::File;
use std::io::{Write, Result as IOResult};

fn save(res_vec: Vec<u8>, to_path: &PathBuf) -> IOResult<()>{
    let mut result_file = try!(File::create(to_path));

    result_file.write_all(&res_vec)
}

const USAGE: &'static str = r#"Delta L encryption program

Usage:
    delta-l <MODE> <FILE> [OPTIONS]
    delta-l -?

Modes:
    e[ncrypt]           Encrypts a file
    d[ecrypt]           Decrypts a file"#;

fn incorrect_syntax(){
    println!("Incorrect syntax:\n    Type delta-l -? for help")
}
<commit_msg>Checksum flag is only available when encrypting<commit_after>extern crate delta_l as dl;
extern crate getopts;

use dl::DeltaL;
use dl::Mode::{Encrypt, Decrypt};
use dl::DeltaLError::{Io, InvalidHeader, ChecksumMismatch};

use std::env;
use std::string::String;

use std::io::ErrorKind::NotFound;

use getopts::Options;

fn main() {
    let args = &env::args().collect::<Vec<String>>()[1..];

    let mut opts = Options::new();
    opts.optflag("?", "help", "Prints this help menu.");
    opts.optopt("p", "", "Encrypts/decrypts with a passphrase.", "<passphrase>");
    opts.optopt("o", "", "Sets the output file.", "<output-file>");
    opts.optflag("y", "yes", "Overwrites output file without prompt, if it already exists.");
    opts.optflag("c", "checksum", "Disables checksum feature when encrypting: - This is read from the header when decrypting.");
    opts.optflag("f", "force", "Forces the resulting file to be created even if the checksums mismatch during decryption.");

    let matches = match opts.parse(args){
        Ok(m) => m,
        Err(_) => return incorrect_syntax(),
    };

    if matches.opt_present("?"){
        return print!("{}", opts.usage(USAGE));
    }

    if matches.free.len() != 2{
        return incorrect_syntax();
    }

    let file_path = &*matches.free[1];

    let mut dl = DeltaL::new(match &*matches.free[0]{
        "e"|"encrypt" => Encrypt{checksum: true},
        "d"|"decrypt" => Decrypt,
        _ => return incorrect_syntax()
    });

    let to_file = matches.opt_str("o");
    let passphrase = matches.opt_str("p");
    let checksum = matches.opt_present("c");
    let force = matches.opt_present("f");
    let force_overwite = matches.opt_present("y");

    if checksum {
        if dl.is_mode_encrypt(){
            dl.set_checksum(false);
        }else{
            println!("Checksum flag is only available when encrypting.\n");
            return incorrect_syntax()
        }
    }

    if let Some(pp) = passphrase{
        dl.set_passphrase(&pp);
    }

    let to: PathBuf = to_file
        .unwrap_or(file_path.to_owned() + dl.get_mode_standard_extension())
        // From `String` into `PathBuf`
        .into();

    if to.exists() && !force_overwite{
        println!("Output file already exists; do you want to overwrite (yes/no)?");

        let stdin = std::io::stdin();

        loop{
            let mut answer = String::new();
            stdin.read_line(&mut answer).unwrap();

            match answer.trim().as_ref(){
                "yes" => break,
                "no"  => return println!("{}cryption has been cancelled.", if dl.is_mode_encrypt() {"En"} else {"De"}),
                _ => println!("Please answer yes or no:"),
            }
        }
    }

    match dl.execute(file_path) {
        Ok(res_vec) => {
            save(res_vec, &to).unwrap();
            println!("Result file has been saved to {}", to.to_str().unwrap_or("<nil>"))
        },
        Err(e) => match e {
            Io(e) => match e.kind(){
                NotFound     => println!("Couldn't find the specified file.\nPlease make sure the file exists."),
                _            => println!("An unknown error occured, encrypting the file:\n{:?}", e)
            },
            InvalidHeader    => println!("Invalid header error:\nThe specified file wasn't a valid .delta file."),
            ChecksumMismatch(res_vec) => if force{
                    println!("Checksum mismatch detetected! Saving anyways because of force flag");
                    save(res_vec, &to).unwrap();
                }else{
                    println!("Decryption failed:\nIncorrect passphrase.")
                }
        },
    }
}

use std::path::PathBuf;
use std::fs::File;
use std::io::{Write, Result as IOResult};

fn save(res_vec: Vec<u8>, to_path: &PathBuf) -> IOResult<()>{
    let mut result_file = try!(File::create(to_path));

    result_file.write_all(&res_vec)
}

const USAGE: &'static str = r#"Delta L encryption program

Usage:
    delta-l <MODE> <FILE> [OPTIONS]
    delta-l -?

Modes:
    e[ncrypt]           Encrypts a file
    d[ecrypt]           Decrypts a file"#;

fn incorrect_syntax(){
    println!("Incorrect syntax:\n    Type delta-l -? for help")
}
<|endoftext|>


<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = command_params.value_of("COMMAND").unwrap();
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port);
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(net::SocketAddr::V4(send_addr),
                 net::SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<commit_msg>Remove net:: prefix<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, UdpSocket};
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: SocketAddr) -> UdpSocket {
    let attempt = UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: SocketAddr, target: SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = command_params.value_of("COMMAND").unwrap();
    let ip = Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = SocketAddrV4::new(ip, numeric_port);
    let send_addr = SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(SocketAddr::V4(send_addr),
                 SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.

//! Command-line interface for the sandboxfs file system.

// Keep these in sync with the list of checks in lib.rs.
#![warn(bad_style, missing_docs)]
#![warn(unused, unused_extern_crates, unused_import_braces, unused_qualifications)]
#![warn(unsafe_code)]

extern crate env_logger;
#[macro_use] extern crate failure;
extern crate getopts;
extern crate sandboxfs;
extern crate time;

use failure::{Fallible, ResultExt};
use getopts::Options;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process;
use std::result::Result;
use time::Timespec;

/// Default value of the `--ttl` flag.
///
/// This is expressed as a string rather than a parsed value to ensure the default value can be
/// parsed with the same semantics as user-provided values.
static DEFAULT_TTL: &str = "60s";

/// Suffix for durations expressed in seconds.
static SECONDS_SUFFIX: &str = "s";

/// Execution failure due to a user-triggered error.
#[derive(Debug, Fail)]
#[fail(display = "{}", message)]
struct UsageError {
    message: String,
}

/// Parses the value of a flag that controls who has access to the mount point.
///
/// Returns the collection of options, if any, to be passed to the FUSE mount operation in order to
/// grant the requested permissions.
fn parse_allow(s: &str) -> Result<&'static [&'static str], UsageError> {
    match s {
        "other" => Ok(&["-o", "allow_other"]),
        "root" => Ok(&["-o", "allow_root"]),
        "self" => Ok(&[]),
        _ => {
            let message = format!("{} must be one of other, root, or self", s);
            Err(UsageError { message })
        },
    }
}

/// Parses the value of a flag that takes a duration, which must specify its unit.
fn parse_duration(s: &str) -> Result<Timespec, UsageError> {
    let (value, unit) = match s.find(|c| !char::is_ascii_digit(&c) && c != '-') {
        Some(pos) => s.split_at(pos),
        None => {
            let message = format!("invalid time specification {}: missing unit", s);
            return Err(UsageError { message });
        },
    };

    if unit != SECONDS_SUFFIX {
        let message = format!(
            "invalid time specification {}: unsupported unit '{}' (only '{}' is allowed)",
            s, unit, SECONDS_SUFFIX);
        return Err(UsageError { message });
    }

    value.parse::<u32>()
        .map(|sec| Timespec { sec: i64::from(sec), nsec: 0 })
        .map_err(|e| UsageError { message: format!("invalid time specification {}: {}", s, e) })
}

/// Takes the list of strings that represent mappings (supplied via multiple instances of the
/// `--mapping` flag) and returns a parsed representation of those flags.
fn parse_mappings<T: AsRef<str>, U: IntoIterator<Item=T>>(args: U)
    -> Result<Vec<sandboxfs::Mapping>, UsageError> {
    let mut mappings = Vec::new();

    for arg in args {
        let arg = arg.as_ref();

        let fields: Vec<&str> = arg.split(':').collect();
        if fields.len() != 3 {
            let message = format!("bad mapping {}: expected three colon-separated fields", arg);
            return Err(UsageError { message });
        }

        let writable = {
            if fields[0] == "ro" {
                false
            } else if fields[0] == "rw" {
                true
            } else {
                let message = format!("bad mapping {}: type was {} but should be ro or rw",
                    arg, fields[0]);
                return Err(UsageError { message });
            }
        };

        let path = PathBuf::from(fields[1]);
        let underlying_path = PathBuf::from(fields[2]);

        match sandboxfs::Mapping::from_parts(path, underlying_path, writable) {
            Ok(mapping) => mappings.push(mapping),
            Err(e) => {
                // TODO(jmmv): Figure how to best leverage failure's cause propagation.  May need
                // to define a custom ErrorKind to represent UsageError, instead of having a special
                // error type.
                let message = format!("bad mapping {}: {}", arg, e);
                return Err(UsageError { message });
            }
        }
    }

    Ok(mappings)
}

/// Obtains the program name from the execution's first argument, or returns a default if the
/// program name cannot be determined for whatever reason.
fn program_name(args: &[String], default: &'static str) -> String {
    let default = String::from(default);
    match args.get(0) {
        Some(arg0) => match Path::new(arg0).file_name() {
            Some(basename) => match basename.to_str() {
                Some(basename) => String::from(basename),
                None => default,
            },
            None => default,
        },
        None => default,
    }
}

/// Parses the value of a flag specifying a file for I/O.
///
/// `value` contains the textual value of the flag, which is returned as a path if present.
/// Otherwise, if `value` is missing or if it matches `-`, then returns `default`.
///
/// Note that, for simplicity, this will reopen any of the standard streams if provided as a path
/// to `/dev/std*`.  We cannot prevent the user from supplying those paths, and handling them as
/// any other makes things much easier than trying to special-case those via the objects exposed
/// by `std::io`.
fn file_flag(value: Option<String>, default: &'static str) -> PathBuf {
    let default = PathBuf::from(default);
    match value {
        Some(path) => if path == "-" {
            default
        } else {
            PathBuf::from(path)
        },
        None => default,
    }
}

/// Prints program usage information to stdout.
fn usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options] MOUNT_POINT", program);
    print!("{}", opts.usage(&brief));
}

/// Prints version information to stdout.
fn version() {
    println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
}

/// Program's entry point.  This is a "safe" version of `main` in the sense that this doesn't
/// directly handle errors: all errors are returned to the caller for consistent reporter to the
/// user depending on their type.
fn safe_main(program: &str, args: &[String]) -> Fallible<()> {
    env_logger::init();

    let mut opts = Options::new();
    opts.optopt("", "allow", concat!("specifies who should have access to the file system",
        " (default: self)"), "other|root|self");
    opts.optflag("", "help", "prints usage information and exits");
    opts.optflagopt("", "input", "where to read reconfiguration data from (- for stdin)", "PATH");
    opts.optmulti("", "mapping", "type and locations of a mapping", "TYPE:PATH:UNDERLYING_PATH");
    opts.optflagopt("", "output", "where to write the reconfiguration status to (- for stdout)",
        "PATH");
    opts.optopt("", "ttl",
        &format!("how long the kernel is allowed to keep file metadata (default: {})", DEFAULT_TTL),
        &format!("TIME{}", SECONDS_SUFFIX));
    opts.optflag("", "version", "prints version information and exits");
    let matches = opts.parse(args)?;

    if matches.opt_present("help") {
        usage(&program, &opts);
        return Ok(());
    }

    if matches.opt_present("version") {
        version();
        return Ok(());
    }

    let mut options = vec!("-o", "fsname=sandboxfs");
    // TODO(jmmv): Support passing in arbitrary FUSE options from the command line, like "-o ro".

    if let Some(value) = matches.opt_str("allow") {
        for arg in parse_allow(&value)? {
            options.push(arg);
        }
    }

    let mappings = parse_mappings(matches.opt_strs("mapping"))?;

    let ttl = match matches.opt_str("ttl") {
        Some(value) => parse_duration(&value)?,
        None => parse_duration(DEFAULT_TTL).expect(
            "default value for flag is not accepted by the parser; this is a bug in the value"),
    };

    let input = {
        // TODO(jmmv): Reopening /dev/stdin is not possible because its permissions don't allow it
        // when using "su" to change the current user.  We should use io::stdin() instead.
        let path = file_flag(matches.opt_str("input"), "/dev/stdin");
        fs::File::open(&path).context(
            format!("Failed to open reconfiguration input '{}'", path.display()))?
    };

    let output = {
        // TODO(jmmv): Reopening /dev/stdout is not possible because its permissions don't allow it
        // when using "su" to change the current user.  We should use io::stdout() instead.
        let path = file_flag(matches.opt_str("output"), "/dev/stdout");
        fs::OpenOptions::new().write(true).open(&path).context(
            format!("Failed to open reconfiguration output '{}'", path.display()))?
    };

    let mount_point = if matches.free.len() == 1 {
        Path::new(&matches.free[0])
    } else {
        return Err(UsageError { message: "invalid number of arguments".to_string() }.into());
    };

    sandboxfs::mount(mount_point, &options, &mappings, ttl, input, output)
        .context(format!("Failed to mount {}", mount_point.display()))?;
    Ok(())
}

/// Program's entry point.  This delegates to `safe_main` for all program logic and is just in
/// charge of consistently formatting and reporting all possible errors to the caller.
fn main() {
    let args: Vec<String> = env::args().collect();
    let program = program_name(&args, "sandboxfs");

    if let Err(err) = safe_main(&program, &args[1..]) {
        if let Some(err) = err.downcast_ref::<UsageError>() {
            eprintln!("Usage error: {}", err);
            eprintln!("Type {} --help for more information", program);
            process::exit(2);
        } else if let Some(err) = err.downcast_ref::<getopts::Fail>() {
            eprintln!("Usage error: {}", err);
            eprintln!("Type {} --help for more information", program);
            process::exit(2);
        } else {
            eprintln!("{}: {}", program, sandboxfs::flatten_causes(&err));
            process::exit(1);
        }
    }
}

#[cfg(test)]
mod tests {
    use sandboxfs::Mapping;
    use super::*;

    /// Checks that an error, once formatted for printing, contains the given substring.
    fn err_contains(substr: &str, err: impl failure::Fail) {
        let formatted = format!("{}", err);
        assert!(formatted.contains(substr),
            "bad error message '{}'; does not contain '{}'", formatted, substr);
    }

    #[test]
    fn test_parse_duration_ok() {
        assert_eq!(Timespec { sec: 1234, nsec: 0 }, parse_duration("1234s").unwrap());
    }

    #[test]
    fn test_parse_duration_bad_unit() {
        err_contains("missing unit", parse_duration("1234").unwrap_err());
        err_contains("unsupported unit 'ms'", parse_duration("1234ms").unwrap_err());
        err_contains("unsupported unit 'ss'", parse_duration("1234ss").unwrap_err());
    }

    #[test]
    fn test_parse_duration_bad_value() {
        err_contains("invalid digit", parse_duration("-5s").unwrap_err());
        err_contains("unsupported unit ' s'", parse_duration("5 s").unwrap_err());
        err_contains("unsupported unit ' 5s'", parse_duration(" 5s").unwrap_err());
    }

    #[test]
    fn test_parse_mappings_ok() {
        let args = ["ro:/:/fake/root", "rw:/foo:/bar"];
        let exp_mappings = vec!(
            Mapping::from_parts(PathBuf::from("/"), PathBuf::from("/fake/root"), false).unwrap(),
            Mapping::from_parts(PathBuf::from("/foo"), PathBuf::from("/bar"), true).unwrap(),
        );
        match parse_mappings(&args) {
            Ok(mappings) => assert_eq!(exp_mappings, mappings),
            Err(e) => panic!(e),
        }
    }

    #[test]
    fn test_parse_mappings_bad_format() {
        for arg in ["", "foo:bar", "foo:bar:baz:extra"].iter() {
            let err = parse_mappings(&[arg]).unwrap_err();
            err_contains(
                &format!("bad mapping {}: expected three colon-separated fields", arg), err);
        }
    }

    #[test]
    fn test_parse_mappings_bad_type() {
        let args = ["rr:/foo:/bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping rr:/foo:/bar: type was rr but should be ro or rw", err);
    }

    #[test]
    fn test_parse_mappings_bad_path() {
        let args = ["ro:foo:/bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping ro:foo:/bar: path \"foo\" is not absolute", err);
    }

    #[test]
    fn test_parse_mappings_bad_underlying_path() {
        let args = ["ro:/foo:bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping ro:/foo:bar: path \"bar\" is not absolute", err);
    }

    #[test]
    fn test_program_name_uses_default_on_errors() {
        assert_eq!("default", program_name(&[], "default"));
    }

    #[test]
    fn test_program_name_uses_file_name_only() {
        assert_eq!("b", program_name(&["a/b".to_string()], "unused"));
        assert_eq!("foo", program_name(&["./x/y/foo".to_string()], "unused"));
    }
}
<commit_msg>Disallow --allow=root on Linux<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.

//! Command-line interface for the sandboxfs file system.

// Keep these in sync with the list of checks in lib.rs.
#![warn(bad_style, missing_docs)]
#![warn(unused, unused_extern_crates, unused_import_braces, unused_qualifications)]
#![warn(unsafe_code)]

extern crate env_logger;
#[macro_use] extern crate failure;
extern crate getopts;
extern crate sandboxfs;
extern crate time;

use failure::{Fallible, ResultExt};
use getopts::Options;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process;
use std::result::Result;
use time::Timespec;

/// Default value of the `--ttl` flag.
///
/// This is expressed as a string rather than a parsed value to ensure the default value can be
/// parsed with the same semantics as user-provided values.
static DEFAULT_TTL: &str = "60s";

/// Suffix for durations expressed in seconds.
static SECONDS_SUFFIX: &str = "s";

/// Execution failure due to a user-triggered error.
#[derive(Debug, Fail)]
#[fail(display = "{}", message)]
struct UsageError {
    message: String,
}

/// Parses the value of a flag that controls who has access to the mount point.
///
/// Returns the collection of options, if any, to be passed to the FUSE mount operation in order to
/// grant the requested permissions.
fn parse_allow(s: &str) -> Fallible<&'static [&'static str]> {
    match s {
        "other" => Ok(&["-o", "allow_other"]),
        "root" => {
            if cfg!(target_os = "linux") {
                // "-o allow_root" is broken on Linux because this is not actually a
                // fusermount option: it is a libfuse option and the Go bindings don't
                // implement it as such.  We could implement this on our own by handling
                // allow_root as if it were allow_other with an explicit user check... but
                // it's probably not worth doing.  For now, just tell the user that we know
                // about the breakage.
                //
                // See https://github.com/bazil/fuse/issues/144 for context (which is about Go
                // but applies equally here).
                Err(format_err!("--allow=root is known to be broken on Linux"))
            } else {
                Ok(&["-o", "allow_root"])
            }
        },
        "self" => Ok(&[]),
        _ => {
            let message = format!("{} must be one of other, root, or self", s);
            Err(UsageError { message }.into())
        },
    }
}

/// Parses the value of a flag that takes a duration, which must specify its unit.
fn parse_duration(s: &str) -> Result<Timespec, UsageError> {
    let (value, unit) = match s.find(|c| !char::is_ascii_digit(&c) && c != '-') {
        Some(pos) => s.split_at(pos),
        None => {
            let message = format!("invalid time specification {}: missing unit", s);
            return Err(UsageError { message });
        },
    };

    if unit != SECONDS_SUFFIX {
        let message = format!(
            "invalid time specification {}: unsupported unit '{}' (only '{}' is allowed)",
            s, unit, SECONDS_SUFFIX);
        return Err(UsageError { message });
    }

    value.parse::<u32>()
        .map(|sec| Timespec { sec: i64::from(sec), nsec: 0 })
        .map_err(|e| UsageError { message: format!("invalid time specification {}: {}", s, e) })
}

/// Takes the list of strings that represent mappings (supplied via multiple instances of the
/// `--mapping` flag) and returns a parsed representation of those flags.
fn parse_mappings<T: AsRef<str>, U: IntoIterator<Item=T>>(args: U)
    -> Result<Vec<sandboxfs::Mapping>, UsageError> {
    let mut mappings = Vec::new();

    for arg in args {
        let arg = arg.as_ref();

        let fields: Vec<&str> = arg.split(':').collect();
        if fields.len() != 3 {
            let message = format!("bad mapping {}: expected three colon-separated fields", arg);
            return Err(UsageError { message });
        }

        let writable = {
            if fields[0] == "ro" {
                false
            } else if fields[0] == "rw" {
                true
            } else {
                let message = format!("bad mapping {}: type was {} but should be ro or rw",
                    arg, fields[0]);
                return Err(UsageError { message });
            }
        };

        let path = PathBuf::from(fields[1]);
        let underlying_path = PathBuf::from(fields[2]);

        match sandboxfs::Mapping::from_parts(path, underlying_path, writable) {
            Ok(mapping) => mappings.push(mapping),
            Err(e) => {
                // TODO(jmmv): Figure how to best leverage failure's cause propagation.  May need
                // to define a custom ErrorKind to represent UsageError, instead of having a special
                // error type.
                let message = format!("bad mapping {}: {}", arg, e);
                return Err(UsageError { message });
            }
        }
    }

    Ok(mappings)
}

/// Obtains the program name from the execution's first argument, or returns a default if the
/// program name cannot be determined for whatever reason.
fn program_name(args: &[String], default: &'static str) -> String {
    let default = String::from(default);
    match args.get(0) {
        Some(arg0) => match Path::new(arg0).file_name() {
            Some(basename) => match basename.to_str() {
                Some(basename) => String::from(basename),
                None => default,
            },
            None => default,
        },
        None => default,
    }
}

/// Parses the value of a flag specifying a file for I/O.
///
/// `value` contains the textual value of the flag, which is returned as a path if present.
/// Otherwise, if `value` is missing or if it matches `-`, then returns `default`.
///
/// Note that, for simplicity, this will reopen any of the standard streams if provided as a path
/// to `/dev/std*`.  We cannot prevent the user from supplying those paths, and handling them as
/// any other makes things much easier than trying to special-case those via the objects exposed
/// by `std::io`.
fn file_flag(value: Option<String>, default: &'static str) -> PathBuf {
    let default = PathBuf::from(default);
    match value {
        Some(path) => if path == "-" {
            default
        } else {
            PathBuf::from(path)
        },
        None => default,
    }
}

/// Prints program usage information to stdout.
fn usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options] MOUNT_POINT", program);
    print!("{}", opts.usage(&brief));
}

/// Prints version information to stdout.
fn version() {
    println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
}

/// Program's entry point.  This is a "safe" version of `main` in the sense that this doesn't
/// directly handle errors: all errors are returned to the caller for consistent reporter to the
/// user depending on their type.
fn safe_main(program: &str, args: &[String]) -> Fallible<()> {
    env_logger::init();

    let mut opts = Options::new();
    opts.optopt("", "allow", concat!("specifies who should have access to the file system",
        " (default: self)"), "other|root|self");
    opts.optflag("", "help", "prints usage information and exits");
    opts.optflagopt("", "input", "where to read reconfiguration data from (- for stdin)", "PATH");
    opts.optmulti("", "mapping", "type and locations of a mapping", "TYPE:PATH:UNDERLYING_PATH");
    opts.optflagopt("", "output", "where to write the reconfiguration status to (- for stdout)",
        "PATH");
    opts.optopt("", "ttl",
        &format!("how long the kernel is allowed to keep file metadata (default: {})", DEFAULT_TTL),
        &format!("TIME{}", SECONDS_SUFFIX));
    opts.optflag("", "version", "prints version information and exits");
    let matches = opts.parse(args)?;

    if matches.opt_present("help") {
        usage(&program, &opts);
        return Ok(());
    }

    if matches.opt_present("version") {
        version();
        return Ok(());
    }

    let mut options = vec!("-o", "fsname=sandboxfs");
    // TODO(jmmv): Support passing in arbitrary FUSE options from the command line, like "-o ro".

    if let Some(value) = matches.opt_str("allow") {
        for arg in parse_allow(&value)? {
            options.push(arg);
        }
    }

    let mappings = parse_mappings(matches.opt_strs("mapping"))?;

    let ttl = match matches.opt_str("ttl") {
        Some(value) => parse_duration(&value)?,
        None => parse_duration(DEFAULT_TTL).expect(
            "default value for flag is not accepted by the parser; this is a bug in the value"),
    };

    let input = {
        // TODO(jmmv): Reopening /dev/stdin is not possible because its permissions don't allow it
        // when using "su" to change the current user.  We should use io::stdin() instead.
        let path = file_flag(matches.opt_str("input"), "/dev/stdin");
        fs::File::open(&path).context(
            format!("Failed to open reconfiguration input '{}'", path.display()))?
    };

    let output = {
        // TODO(jmmv): Reopening /dev/stdout is not possible because its permissions don't allow it
        // when using "su" to change the current user.  We should use io::stdout() instead.
        let path = file_flag(matches.opt_str("output"), "/dev/stdout");
        fs::OpenOptions::new().write(true).open(&path).context(
            format!("Failed to open reconfiguration output '{}'", path.display()))?
    };

    let mount_point = if matches.free.len() == 1 {
        Path::new(&matches.free[0])
    } else {
        return Err(UsageError { message: "invalid number of arguments".to_string() }.into());
    };

    sandboxfs::mount(mount_point, &options, &mappings, ttl, input, output)
        .context(format!("Failed to mount {}", mount_point.display()))?;
    Ok(())
}

/// Program's entry point.  This delegates to `safe_main` for all program logic and is just in
/// charge of consistently formatting and reporting all possible errors to the caller.
fn main() {
    let args: Vec<String> = env::args().collect();
    let program = program_name(&args, "sandboxfs");

    if let Err(err) = safe_main(&program, &args[1..]) {
        if let Some(err) = err.downcast_ref::<UsageError>() {
            eprintln!("Usage error: {}", err);
            eprintln!("Type {} --help for more information", program);
            process::exit(2);
        } else if let Some(err) = err.downcast_ref::<getopts::Fail>() {
            eprintln!("Usage error: {}", err);
            eprintln!("Type {} --help for more information", program);
            process::exit(2);
        } else {
            eprintln!("{}: {}", program, sandboxfs::flatten_causes(&err));
            process::exit(1);
        }
    }
}

#[cfg(test)]
mod tests {
    use sandboxfs::Mapping;
    use super::*;

    /// Checks that an error, once formatted for printing, contains the given substring.
    fn err_contains(substr: &str, err: impl failure::Fail) {
        let formatted = format!("{}", err);
        assert!(formatted.contains(substr),
            "bad error message '{}'; does not contain '{}'", formatted, substr);
    }

    #[test]
    fn test_parse_duration_ok() {
        assert_eq!(Timespec { sec: 1234, nsec: 0 }, parse_duration("1234s").unwrap());
    }

    #[test]
    fn test_parse_duration_bad_unit() {
        err_contains("missing unit", parse_duration("1234").unwrap_err());
        err_contains("unsupported unit 'ms'", parse_duration("1234ms").unwrap_err());
        err_contains("unsupported unit 'ss'", parse_duration("1234ss").unwrap_err());
    }

    #[test]
    fn test_parse_duration_bad_value() {
        err_contains("invalid digit", parse_duration("-5s").unwrap_err());
        err_contains("unsupported unit ' s'", parse_duration("5 s").unwrap_err());
        err_contains("unsupported unit ' 5s'", parse_duration(" 5s").unwrap_err());
    }

    #[test]
    fn test_parse_mappings_ok() {
        let args = ["ro:/:/fake/root", "rw:/foo:/bar"];
        let exp_mappings = vec!(
            Mapping::from_parts(PathBuf::from("/"), PathBuf::from("/fake/root"), false).unwrap(),
            Mapping::from_parts(PathBuf::from("/foo"), PathBuf::from("/bar"), true).unwrap(),
        );
        match parse_mappings(&args) {
            Ok(mappings) => assert_eq!(exp_mappings, mappings),
            Err(e) => panic!(e),
        }
    }

    #[test]
    fn test_parse_mappings_bad_format() {
        for arg in ["", "foo:bar", "foo:bar:baz:extra"].iter() {
            let err = parse_mappings(&[arg]).unwrap_err();
            err_contains(
                &format!("bad mapping {}: expected three colon-separated fields", arg), err);
        }
    }

    #[test]
    fn test_parse_mappings_bad_type() {
        let args = ["rr:/foo:/bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping rr:/foo:/bar: type was rr but should be ro or rw", err);
    }

    #[test]
    fn test_parse_mappings_bad_path() {
        let args = ["ro:foo:/bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping ro:foo:/bar: path \"foo\" is not absolute", err);
    }

    #[test]
    fn test_parse_mappings_bad_underlying_path() {
        let args = ["ro:/foo:bar"];
        let err = parse_mappings(&args).unwrap_err();
        err_contains("bad mapping ro:/foo:bar: path \"bar\" is not absolute", err);
    }

    #[test]
    fn test_program_name_uses_default_on_errors() {
        assert_eq!("default", program_name(&[], "default"));
    }

    #[test]
    fn test_program_name_uses_file_name_only() {
        assert_eq!("b", program_name(&["a/b".to_string()], "unused"));
        assert_eq!("foo", program_name(&["./x/y/foo".to_string()], "unused"));
    }
}
<|endoftext|>


<commit_before>extern crate bufstream;

use std::net::{TcpListener, TcpStream};
use std::io::{Write, BufRead, Read};
use bufstream::BufStream;
use std::error::Error;
use std::process::{Command, Stdio};

fn main(){
    let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
    // accept connections and process them, spawning a new thread for each one
    for stream in listener.incoming() {
        match stream {
            Err(why) => println!("accept err {}", why.description()),
            Ok(stream) => {
                handler(&mut BufStream::new(stream));
            }
        }
    }
}

fn echo_err(why: std::io::Error, s: &mut BufStream<TcpStream>){
    let _ = s.write_all(why.description().as_bytes());
    let _ = s.write_all(b"\n");
}

fn echo_str(str: std::string::String, s: &mut BufStream<TcpStream>){
    let _ = s.write_all(str.as_bytes());
    let _ = s.write_all(b"\n");
    let _ = s.flush();
}

fn handler(s: &mut BufStream<TcpStream>){
    // Until the user does not request to close.
    loop{
        let _ = s.write(b"> ");
        let _ = s.flush();
        let mut cmd = std::string::String::new();
        let _ = s.read_line(&mut cmd).unwrap();
        let cmd_splt : Vec<_> = cmd.as_str().split_whitespace().collect();
        let mut args = Vec::new();
        for i in 1..cmd_splt.len() {
            args.push(cmd_splt[i]);
        }

        match Command::new(cmd_splt[0]).args(&args).stdout(Stdio::piped()).spawn() {
            Err(why) => echo_err(why, s),
            Ok(process) => {
                let mut stde = String::new();
                match process.stderr {
                    Some(mut err) => {
                        match err.read_to_string(&mut stde) {
                            Err(why) => echo_err(why, s),
                            Ok(_) => echo_str(stde, s),
                        };
                    },
                    None => {
                        let mut stdo = String::new();
                        match process.stdout.unwrap().read_to_string(&mut stdo) {
                            Err(why) => echo_err(why, s),
                            Ok(_) => echo_str(stdo, s),
                        }
                    }
                }
            },
        };
    }
}
<commit_msg>Making progress<commit_after>extern crate bufstream;

use std::net::{TcpListener, TcpStream};
use std::io::{Write, BufRead, Read};
use bufstream::BufStream;
use std::error::Error;
use std::process::{Command, Stdio};

fn main(){
    let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
    // accept connections and process them, spawning a new thread for each one
    for stream in listener.incoming() {
        match stream {
            Err(why) => println!("accept err {}", why.description()),
            Ok(stream) => {
                handler(&mut BufStream::new(stream));
            }
        }
    }
}

fn echo_err(why: std::io::Error, s: &mut BufStream<TcpStream>)-> std::io::Result<()>{
    s.write_all(why.description().as_bytes());
    s.write_all(b"\n")
}

fn echo_str(str: std::string::String, s: &mut BufStream<TcpStream>) -> std::io::Result<()>{
    //s.write_all(str.as_bytes()).and_then(s.write_all(b"\n").and_then(s.flush()));
    s.write_all(str.as_bytes());
    s.write_all(b"\n");
    s.flush()
}

fn handler(s: &mut BufStream<TcpStream>){
    // Until the user does not request to close.
    loop{
        let _ = s.write(b"> ");
        let _ = s.flush();
        let mut cmd = std::string::String::new();
        s.read_line(&mut cmd).unwrap();
        let cmd_splt : Vec<_> = cmd.as_str().split_whitespace().collect();
        let mut args = Vec::new();
        for i in 1..cmd_splt.len() {
            args.push(cmd_splt[i]);
        }
        let main_cmd = cmd_splt[0];
        // Here goes the command processing.
        match main_cmd{
            "exit" => break,
            _ => match Command::new(main_cmd).args(&args).stdout(Stdio::piped()).spawn() {
                Err(why) => {
                    match echo_err(why, s){
                        Err(why) => println!("could not send error {:?}", why),
                        Ok(_) =>{}
                    }
                },
                Ok(process) => {
                    let mut stde = String::new();
                    match process.stderr {
                        Some(mut err) => {
                            match err.read_to_string(&mut stde) {
                                Err(why) => match echo_err(why, s){
                                    Err(why) => println!("could not send error {:?}", why),
                                    Ok(_) =>{}
                                },
                                Ok(_) => match echo_str(stde, s){
                                    Err(why) => println!("{:?}", why),
                                    Ok(_) =>{}
                                },
                            };
                        },
                        None => {
                            let mut stdo = String::new();
                            match process.stdout.unwrap().read_to_string(&mut stdo) {
                                Err(why) => match echo_err(why, s){
                                    Err(why) => println!("could not send error {:?}", why),
                                    Ok(_) =>{}
                                },
                                Ok(_) => match echo_str(stdo, s){
                                    Err(why) => println!("{:?}", why),
                                    Ok(_) =>{}
                                },
                            }
                        }
                    }
                },
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const UNIVERSE_SIZE: usize = 510;

const GAMMA: f32 = 2.2;

#[derive(Debug)]
struct Params {
    decay:         f32,
    threshold:     f32,
    max_intensity: f32,
    sleep:         Duration
}

#[derive(Debug)]
#[derive(Clone)]
struct RGB {
    red:   u8,
    green: u8,
    blue:  u8
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { intensity: f32, age: u32, temp: u16, rgb: RGB }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn build_params () -> Params {
    // seed default params
    let mut params = Params { decay: 0.002, threshold: 0.001, max_intensity: 0.8_f32, sleep: Duration::new(0, 20_000_000) };

    // parse command line args and adjust params accordingly
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxintensity", "maximum brightness, 1..255, default 75", "MAX");
    opts.optopt("s", "sleep", "sleep interval in seconds, default 1.5", "SECONDS");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        let max: u8 = matches.opt_str("m").unwrap().parse::<u8>().unwrap();
        params.max_intensity = (max as f32)/255_f32
    }
    if matches.opt_present("s") {
        // take float seconds
        // convert to int seconds and nanoseconds to make Duration happy
        let seconds: f32 = matches.opt_str("s").unwrap().parse::<f32>().unwrap();
        let whole_seconds: u64 = seconds as u64;
        let nano_seconds: u32 = ((seconds - whole_seconds as f32) * 1_000_000_000_f32) as u32;
        params.sleep = Duration::new(whole_seconds, nano_seconds);
    }
    return params;
}

fn kelvin (mut temp: u16) -> RGB {
    // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
    temp /= 100;

    let mut rgb: RGB = RGB { red: 0, green: 0, blue: 0 };
    // calculate red
    if temp <= 66 {
        rgb.red = 255;
    } else {
        let red: f32 = (temp - 60) as f32;
        rgb.red = (329.698727446 * red.powf(-0.1332047592)).round() as u8;
    }
    // calculate green
    if temp <= 66 {
        let green: f32 = temp as f32;
        rgb.green = (99.4708025861 * green.ln() - 161.1195681661).round() as u8;
    } else {
        let green: f32 = (temp - 60) as f32;
        rgb.green = (288.1221695283 * green.powf(-0.0755148492)).round() as u8;
    }
    // calculate blue
    if temp >= 66 {
        rgb.blue = 255;
    } else {
        if temp <= 19 {
            rgb.blue = 0;
        } else {
            let blue: f32 = (temp - 10) as f32;
            rgb.blue = (138.5177312231 * blue.ln() - 305.0447927307).round() as u8;
        }
    }
    return rgb;
}

fn gamma_correct(rgb: &RGB) -> RGB {
    let mut c: RGB = RGB {red: 0, green: 0, blue: 0 };
    c.red   = (255_f32 * (rgb.red   as f32 / 255_f32).powf(GAMMA)) as u8;
    c.green = (255_f32 * (rgb.green as f32 / 255_f32).powf(GAMMA)) as u8;
    c.blue  = (255_f32 * (rgb.blue  as f32 / 255_f32).powf(GAMMA)) as u8;
    return c;
}

fn scale_rgb(rgb: RGB, intensity: f32, params: &Params) -> RGB {
    let i: f32 = intensity * params.max_intensity;
    let scaled: RGB = RGB {
        red:   (rgb.red   as f32 * i).round() as u8,
        green: (rgb.green as f32 * i).round() as u8,
        blue:  (rgb.blue  as f32 * i).round() as u8
    };
    return scaled;
}

fn main() {
    let params = build_params();

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 0, body: 44, tail: 3, name: "10".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    // TODO: probably a more idiomatic way to built the default state
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel {
                intensity: 0_f32,
                age: 0,
                temp: 0,
                rgb: RGB { red: 0, green: 0, blue: 0 },
            };
            lights.push(pixel);
        }
    }

    let mut rng = rand::thread_rng();
    let zero_to_one = Range::new(0_f32, 1_f32);
    let temp_range = Range::new(2700_f32, 5500_f32);

    loop {
        for light in lights.iter_mut() {
            if light.intensity == 0_f32 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.intensity = zero_to_one.ind_sample(&mut rng) as f32;
                    light.age += 1;
                    light.temp = temp_range.ind_sample(&mut rng).round() as u16;
                    light.rgb = scale_rgb(kelvin(light.temp), light.intensity, &params);
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.intensity -= (zero_to_one.ind_sample(&mut rng) * params.decay) as f32;
                    light.age += 1;
                    // test to see if we bottomed out
                    // check if intensity fell below zero
                    if light.intensity <= 0_f32 {
                        light.intensity = 0_f32;
                        light.age = 0;
                        light.temp = 0;
                        light.rgb.red = 0;
                        light.rgb.green = 0;
                        light.rgb.blue = 0;
                    }
                    // count zeroes, to avoid single-color output
                    //   when gamma-correct bottoms out, usually with red
                    let mut zeroes: u8 = 0;
                    if light.rgb.red == 0 { zeroes += 1 };
                    if light.rgb.green == 0 { zeroes += 1 };
                    if light.rgb.blue == 0 { zeroes += 1 };
                    if zeroes >= 2 {
                        light.intensity = 0_f32;
                        light.age = 0;
                        light.temp = 0;
                        light.rgb.red = 0;
                        light.rgb.green = 0;
                        light.rgb.blue = 0;
                    }
                } else {
                    // rising
                    light.intensity += zero_to_one.ind_sample(&mut rng) * params.decay * (params.max_intensity - light.intensity);

                    light.age += 1;
                    light.rgb = scale_rgb(kelvin(light.temp), light.intensity, &params);
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(params.sleep);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 0..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..zone.body {
            let ref rgb = copy[idx].rgb;
            let gc = gamma_correct(&rgb);
            out.push(gc.red);
            out.push(gc.green);
            out.push(gc.blue);
            idx += 1;
            // HACK: patching up an off-by-one somewhere
            if idx == copy.len() { break };
        }
        // null pixels at tail
        for i in 0..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(out);
        out = u;
    }
    universes.push(out);
    let mut universe: u16 = 1;
    for u in universes {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<commit_msg>still chasing bottom out, checking post-gamma<commit_after>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const UNIVERSE_SIZE: usize = 510;

const GAMMA: f32 = 2.2;
const BOTTOM_THRESHOLD: u16 = 10;

#[derive(Debug)]
struct Params {
    decay:         f32,
    threshold:     f32,
    max_intensity: f32,
    sleep:         Duration
}

#[derive(Debug)]
#[derive(Clone)]
struct RGB {
    red:   u8,
    green: u8,
    blue:  u8
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { intensity: f32, age: u32, temp: u16, rgb: RGB }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn build_params () -> Params {
    // seed default params
    let mut params = Params { decay: 0.002, threshold: 0.001, max_intensity: 0.8_f32, sleep: Duration::new(0, 20_000_000) };

    // parse command line args and adjust params accordingly
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxintensity", "maximum brightness, 1..255, default 75", "MAX");
    opts.optopt("s", "sleep", "sleep interval in seconds, default 1.5", "SECONDS");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        let max: u8 = matches.opt_str("m").unwrap().parse::<u8>().unwrap();
        params.max_intensity = (max as f32)/255_f32
    }
    if matches.opt_present("s") {
        // take float seconds
        // convert to int seconds and nanoseconds to make Duration happy
        let seconds: f32 = matches.opt_str("s").unwrap().parse::<f32>().unwrap();
        let whole_seconds: u64 = seconds as u64;
        let nano_seconds: u32 = ((seconds - whole_seconds as f32) * 1_000_000_000_f32) as u32;
        params.sleep = Duration::new(whole_seconds, nano_seconds);
    }
    return params;
}

fn kelvin (mut temp: u16) -> RGB {
    // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
    temp /= 100;

    let mut rgb: RGB = RGB { red: 0, green: 0, blue: 0 };
    // calculate red
    if temp <= 66 {
        rgb.red = 255;
    } else {
        let red: f32 = (temp - 60) as f32;
        rgb.red = (329.698727446 * red.powf(-0.1332047592)).round() as u8;
    }
    // calculate green
    if temp <= 66 {
        let green: f32 = temp as f32;
        rgb.green = (99.4708025861 * green.ln() - 161.1195681661).round() as u8;
    } else {
        let green: f32 = (temp - 60) as f32;
        rgb.green = (288.1221695283 * green.powf(-0.0755148492)).round() as u8;
    }
    // calculate blue
    if temp >= 66 {
        rgb.blue = 255;
    } else {
        if temp <= 19 {
            rgb.blue = 0;
        } else {
            let blue: f32 = (temp - 10) as f32;
            rgb.blue = (138.5177312231 * blue.ln() - 305.0447927307).round() as u8;
        }
    }
    return rgb;
}

fn gamma_correct(rgb: &RGB) -> RGB {
    let mut c: RGB = RGB {red: 0, green: 0, blue: 0 };
    // short-circuit on null input
    if rgb.red as u16 + rgb.green as u16 + rgb.blue as u16 == 0 {
        return c;
    }
    c.red   = (255_f32 * (rgb.red   as f32 / 255_f32).powf(GAMMA)) as u8;
    c.green = (255_f32 * (rgb.green as f32 / 255_f32).powf(GAMMA)) as u8;
    c.blue  = (255_f32 * (rgb.blue  as f32 / 255_f32).powf(GAMMA)) as u8;
    // drop to null if the sum of all three color values falls below threshold
    if (c.red as u16 + c.green as u16 + c.blue as u16) < BOTTOM_THRESHOLD {
        // println!("{:?} bottomed out to {:?}", rgb, c);
        c.red   = 0;
        c.green = 0;
        c.blue  = 0;
    }
    return c;
}

fn scale_rgb(rgb: RGB, intensity: f32, params: &Params) -> RGB {
    let i: f32 = intensity * params.max_intensity;
    let scaled: RGB = RGB {
        red:   (rgb.red   as f32 * i).round() as u8,
        green: (rgb.green as f32 * i).round() as u8,
        blue:  (rgb.blue  as f32 * i).round() as u8
    };
    return scaled;
}

fn main() {
    let params = build_params();

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 0, body: 44, tail: 3, name: "10".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    // TODO: probably a more idiomatic way to built the default state
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel {
                intensity: 0_f32,
                age: 0,
                temp: 0,
                rgb: RGB { red: 0, green: 0, blue: 0 },
            };
            lights.push(pixel);
        }
    }

    let mut rng = rand::thread_rng();
    let zero_to_one = Range::new(0_f32, 1_f32);
    let temp_range = Range::new(2700_f32, 5500_f32);

    loop {
        for light in lights.iter_mut() {
            if light.intensity == 0_f32 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.intensity = zero_to_one.ind_sample(&mut rng) as f32;
                    light.age += 1;
                    light.temp = temp_range.ind_sample(&mut rng).round() as u16;
                    light.rgb = scale_rgb(kelvin(light.temp), light.intensity, &params);
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.intensity -= (zero_to_one.ind_sample(&mut rng) * params.decay) as f32;
                    light.age += 1;
                    // count zeroes, to avoid single-color output
                    //   when gamma-correct bottoms out, usually with red
                    let mut zeroes: u8 = 0;
                    if light.rgb.red == 0 { zeroes += 1 };
                    if light.rgb.green == 0 { zeroes += 1 };
                    if light.rgb.blue == 0 { zeroes += 1 };
                    if zeroes >= 2 {
                        light.intensity = 0_f32;
                        light.age = 0;
                        light.temp = 0;
                        light.rgb.red = 0;
                        light.rgb.green = 0;
                        light.rgb.blue = 0;
                    }
                } else {
                    // rising
                    light.intensity += zero_to_one.ind_sample(&mut rng) * params.decay * (params.max_intensity - light.intensity);

                    light.age += 1;
                    light.rgb = scale_rgb(kelvin(light.temp), light.intensity, &params);
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(params.sleep);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 0..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..zone.body {
            let ref rgb = copy[idx].rgb;
            let gc = gamma_correct(&rgb);
            out.push(gc.red);
            out.push(gc.green);
            out.push(gc.blue);
            idx += 1;
            // HACK: patching up an off-by-one somewhere
            if idx == copy.len() { break };
        }
        // null pixels at tail
        for i in 0..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(out);
        out = u;
    }
    universes.push(out);
    let mut universe: u16 = 1;
    for u in universes {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<|endoftext|>


<commit_before>#![feature(lang_items, unboxed_closures)]

use std::io::{File, Truncate, Write};
use std::mem;
use std::num::Float;
use std::num::FloatMath;

struct SinWave(pub f64);

impl Fn<(f64, ), f64> for SinWave {
    extern "rust-call" fn call(&self, (x, ): (f64, )) -> f64 {
        let SinWave(period) = *self;
        FloatMath::sin(x * period)
    }
}

fn main() {
    println!("Hello, synthrs!");

    let mut bytes: [u8, ..44100] = unsafe { mem::uninitialized() };
    for i in range(0u, 44100) {
        bytes[i] = quantize_8bit(generate(i as f64, SinWave(1000.0)))
        // bytes[i] = generate(i as f64, |x: f64| {
        //    FloatMath::sin(x / 8000.0)
        // });
    }

    write_pcm("test.pcm", &bytes);
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_8bit(y: f64) -> u8 {
    let step = 2.0 / 8.0; // Max range [-1, 1], 8 bits
    (((y + 1.0) / step) * 8.0).floor() as u8 // Convert to [0, 7]
}

fn write_pcm(filename: &str, bytes: &[u8]) {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    f.write(bytes);
}
<commit_msg>Squarewave generation<commit_after>#![feature(lang_items, unboxed_closures)]

use std::io::{File, Truncate, Write};
use std::mem;
use std::num::Float;
use std::num::FloatMath;

struct SinWave(pub f64);

impl Fn<(f64, ), f64> for SinWave {
    extern "rust-call" fn call(&self, (x, ): (f64, )) -> f64 {
        let SinWave(period) = *self;
        FloatMath::sin(x * period)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (x, ): (f64, )) -> f64 {
        let SquareWave(period) = *self;
        let sin_wave = SinWave(period);
        if sin_wave(x).is_positive() { 1.0 } else { -1.0 }
    }
}

fn main() {
    println!("Hello, synthrs!");

    let mut bytes: [u8, ..44100] = unsafe { mem::uninitialized() };
    for i in range(0u, 44100) {
        // bytes[i] = quantize_8bit(generate(i as f64, SinWave(1000.0)))
        bytes[i] = quantize_8bit(generate(i as f64, SquareWave(1000.0)))
        // bytes[i] = generate(i as f64, |x: f64| {
        //    FloatMath::sin(x / 8000.0)
        // });
    }

    write_pcm("test.pcm", &bytes);
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_8bit(y: f64) -> u8 {
    let step = 2.0 / 8.0; // Max range [-1, 1], 8 bits
    (((y + 1.0) / step) * 8.0).floor() as u8 // Convert to [0, 7]
}

fn write_pcm(filename: &str, bytes: &[u8]) {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    f.write(bytes);
}
<|endoftext|>


<commit_before>#[derive(Clone, Copy, Debug)]
enum PBType {
    Integer64,
    String,
}

#[derive(Clone, Debug)]
struct PBField {
    name: String,
    tag: u8,
    field_type: PBType,
}

#[derive(Clone, Debug)]
struct PBMessage {
    name: String,
    fields: Vec<PBField>,
}

#[derive(Clone, Copy, Debug)]
enum PBWireType {
    VarInt,
    Fixed64,
    LengthDelim,
    Fixed32,
    Reserved,
}

struct TagValue<'a> {
    tag: u8,
    wire_type: PBWireType,
    value: &'a [u8],
}

#[derive(Clone)]
struct ParserPosition<'a> {
    slice: &'a [u8],
    tag: u8,
    wire_type: PBWireType,
}

trait InitParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a>;
}

impl<'a> InitParserPosition<'a> for ParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a> {
        ParserPosition {
            slice: msg_slice,
            tag: 0,
            wire_type: PBWireType::Reserved,
        }
    }
}

static IDX2WIRE_TYPE: &'static [PBWireType; 6] = &[PBWireType::VarInt,
                                                   PBWireType::Fixed64,
                                                   PBWireType::LengthDelim,
                                                   PBWireType::Reserved,
                                                   PBWireType::Reserved,
                                                   PBWireType::Fixed32];
#[derive(Clone, Copy, Debug)]
enum PBParseError {
    UnknownWireType(usize),
}

fn PBParseNext<'a>(pos: ParserPosition<'a>)
                   -> Result<(TagValue<'a>, ParserPosition<'a>), PBParseError> {
    let mut p = pos.clone();

    p.tag = p.slice[0] >> 3;
    let wire_type_idx = (p.slice[0] & 7) as usize;
    if wire_type_idx >= IDX2WIRE_TYPE.len() {
        return Err(PBParseError::UnknownWireType(wire_type_idx));
    }
    p.wire_type = IDX2WIRE_TYPE[wire_type_idx];

    let mut value = match p.wire_type {
        PBWireType::LengthDelim => {
            let offset = p.slice[1] as usize;
            let start = 2;
            let end = 2 + offset;
            let x = &p.slice[start..end];
            p.slice = &p.slice[end..];
            x
        }
        _ => &[],
    };
    Ok((TagValue {
            value: value,
            tag: p.tag,
            wire_type: p.wire_type,
        },
        p.clone()))
}

use std::str;

#[derive(Debug)]
enum ReprError {
    InvalidWireAndSchemaType,
    Utf8Error(std::str::Utf8Error),
}

fn StringRepr<'a>(wire_type: PBWireType,
                  schema_type: PBType,
                  bytes: &'a [u8])
                  -> Result<String, ReprError> {
    match (wire_type, schema_type) {
        (PBWireType::LengthDelim, PBType::String) => {
            let x = str::from_utf8(bytes);
            match x {
                Ok(y) => Ok(String::from(y)),
                Err(y) => Err(ReprError::Utf8Error(y)),
            }
        }
        (PBWireType::VarInt, PBType::Integer64) => {
          match IntegerRepr(wire_type, schema_type, bytes) {
            Ok(i) => Ok(String::from(format!("{}", i))),
            Err(e) => Err(e)
          }
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn IntegerRepr<'a>(wire_type: PBWireType,
                   schema_type: PBType,
                   bytes: &'a [u8])
                   -> Result<i64, ReprError> {

    extern crate num;
    match (wire_type, schema_type) {
        (PBWireType::VarInt, PBType::Integer64) => {
            let mut pos: u8 = 0;
            let mut res: i64 = 0;
            for b in bytes {
                // I'm assuming I have an endieness problem here?
                for i in 0..7 {
                  let bit_shift = 6-i;
                  let pow_add = pos + 6 - i;
                  if ((b >> (6-i)) & 1u8) == 1u8 {
                    res += num::pow(2, (pow_add) as usize);
                  }
                }
                pos += 7;
            }
            Ok(res)
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn main() {
    /*
     * Sample ProtocolBuffer message for schema:
     *
     *     message Test2 {
     *       required string a = 1;
     *       required string b = 2;
     *     }
     *
     * with a = test, b = testing
     *
     * @see https://developers.google.com/protocol-buffers/docs/encoding
     */
    let msg = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x04, 0x74, 0x65,
                   0x73, 0x74];
    let msg_slice = msg.as_slice();

    let (mut x, mut pp) = PBParseNext(ParserPosition::init(msg_slice)).unwrap();

    print!("Tag   = {}\n", x.tag);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {}\n", x.tag);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    print!("300 = {}\n",
           IntegerRepr(PBWireType::VarInt, PBType::Integer64, &[0xacu8, 0x02u8]).unwrap());
    print!("300 = string {}\n",
           StringRepr(PBWireType::VarInt, PBType::Integer64, &[0xacu8, 0x02u8]).unwrap());
}
<commit_msg>rustfmt<commit_after>#[derive(Clone, Copy, Debug)]
enum PBType {
    Integer64,
    String,
}

#[derive(Clone, Debug)]
struct PBField {
    name: String,
    tag: u8,
    field_type: PBType,
}

#[derive(Clone, Debug)]
struct PBMessage {
    name: String,
    fields: Vec<PBField>,
}

#[derive(Clone, Copy, Debug)]
enum PBWireType {
    VarInt,
    Fixed64,
    LengthDelim,
    Fixed32,
    Reserved,
}

struct TagValue<'a> {
    tag: u8,
    wire_type: PBWireType,
    value: &'a [u8],
}

#[derive(Clone)]
struct ParserPosition<'a> {
    slice: &'a [u8],
    tag: u8,
    wire_type: PBWireType,
}

trait InitParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a>;
}

impl<'a> InitParserPosition<'a> for ParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a> {
        ParserPosition {
            slice: msg_slice,
            tag: 0,
            wire_type: PBWireType::Reserved,
        }
    }
}

static IDX2WIRE_TYPE: &'static [PBWireType; 6] = &[PBWireType::VarInt,
                                                   PBWireType::Fixed64,
                                                   PBWireType::LengthDelim,
                                                   PBWireType::Reserved,
                                                   PBWireType::Reserved,
                                                   PBWireType::Fixed32];
#[derive(Clone, Copy, Debug)]
enum PBParseError {
    UnknownWireType(usize),
}

fn PBParseNext<'a>(pos: ParserPosition<'a>)
                   -> Result<(TagValue<'a>, ParserPosition<'a>), PBParseError> {
    let mut p = pos.clone();

    p.tag = p.slice[0] >> 3;
    let wire_type_idx = (p.slice[0] & 7) as usize;
    if wire_type_idx >= IDX2WIRE_TYPE.len() {
        return Err(PBParseError::UnknownWireType(wire_type_idx));
    }
    p.wire_type = IDX2WIRE_TYPE[wire_type_idx];

    let mut value = match p.wire_type {
        PBWireType::LengthDelim => {
            let offset = p.slice[1] as usize;
            let start = 2;
            let end = 2 + offset;
            let x = &p.slice[start..end];
            p.slice = &p.slice[end..];
            x
        }
        _ => &[],
    };
    Ok((TagValue {
            value: value,
            tag: p.tag,
            wire_type: p.wire_type,
        },
        p.clone()))
}

use std::str;

#[derive(Debug)]
enum ReprError {
    InvalidWireAndSchemaType,
    Utf8Error(std::str::Utf8Error),
}

fn StringRepr<'a>(wire_type: PBWireType,
                  schema_type: PBType,
                  bytes: &'a [u8])
                  -> Result<String, ReprError> {
    match (wire_type, schema_type) {
        (PBWireType::LengthDelim, PBType::String) => {
            let x = str::from_utf8(bytes);
            match x {
                Ok(y) => Ok(String::from(y)),
                Err(y) => Err(ReprError::Utf8Error(y)),
            }
        }
        (PBWireType::VarInt, PBType::Integer64) => {
            match IntegerRepr(wire_type, schema_type, bytes) {
                Ok(i) => Ok(String::from(format!("{}", i))),
                Err(e) => Err(e),
            }
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn IntegerRepr<'a>(wire_type: PBWireType,
                   schema_type: PBType,
                   bytes: &'a [u8])
                   -> Result<i64, ReprError> {

    extern crate num;
    match (wire_type, schema_type) {
        (PBWireType::VarInt, PBType::Integer64) => {
            let mut pos: u8 = 0;
            let mut res: i64 = 0;
            for b in bytes {
                // I'm assuming I have an endieness problem here?
                for i in 0..7 {
                    let bit_shift = 6 - i;
                    let pow_add = pos + 6 - i;
                    if ((b >> (6 - i)) & 1u8) == 1u8 {
                        res += num::pow(2, (pow_add) as usize);
                    }
                }
                pos += 7;
            }
            Ok(res)
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn main() {
    /*
     * Sample ProtocolBuffer message for schema:
     *
     *     message Test2 {
     *       required string a = 1;
     *       required string b = 2;
     *     }
     *
     * with a = test, b = testing
     *
     * @see https://developers.google.com/protocol-buffers/docs/encoding
     */
    let msg = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x04, 0x74, 0x65,
                   0x73, 0x74];
    let msg_slice = msg.as_slice();

    let (mut x, mut pp) = PBParseNext(ParserPosition::init(msg_slice)).unwrap();

    print!("Tag   = {}\n", x.tag);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {}\n", x.tag);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    print!("300 = {}\n",
           IntegerRepr(PBWireType::VarInt, PBType::Integer64, &[0xacu8, 0x02u8]).unwrap());
    print!("300 = string {}\n",
           StringRepr(PBWireType::VarInt, PBType::Integer64, &[0xacu8, 0x02u8]).unwrap());
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate chan;
extern crate crypto;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use walkdir::WalkDir;
use std::thread;
use std::sync::Arc;
use walkdir::DirEntry;

fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

type Result<T> = std::result::Result<T, TreesumError>;

#[derive(Debug)]
enum TreesumError {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

fn process_dir_entry(e: walkdir::Result<DirEntry>) -> Result<String> {
    let e = e.map_err(TreesumError::WalkDir)?;
    if !e.file_type().is_file() {
        return Err(TreesumError::Ignored(e));
    }
    let mut hasher = Sha1::new();
    let mut buf = [0; 1024 * 8];
    calc_hash(e.path(), &mut hasher, &mut buf).map_err(TreesumError::Io)
}

fn do_it_2(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let iter_factory = || WalkDir::new(pb);
    let results = scatter_gather(iter_factory, process_dir_entry);
    for r in results {
        println!("{:?}", r);
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    do_it_2(root).expect("Hello error 2!");
}

/// The purpose of this newtype is to hide the channel implementation.
struct GatherIter<T>(chan::Iter<T>);
impl<T> Iterator for GatherIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        self.0.next()
    }
}

/// factory is needed to allow iterators without std::marker::Send
/// TODO: spawn worker threads
/// TODO: use channels
/// TODO: return channel Receiver as Iterator
fn scatter_gather<F, X, J, R, I>(factory: F, xform: X) -> GatherIter<R>
    where F: 'static + std::marker::Send + FnOnce() -> I,
          X: 'static + std::marker::Send + std::marker::Sync + Fn(J) -> R,
          J: 'static + std::marker::Send,
          R: 'static + std::marker::Send,
          I: IntoIterator<Item = J>
{
    let jobs_rx = {
        let (tx, rx) = chan::sync(0);
        thread::spawn(move || {
            for e in factory().into_iter() {
                tx.send(e);
            }
        });
        rx
    };
    let results_rx = {
        let (tx, rx) = chan::sync(0);
        let xform = Arc::new(xform); // TODO: Investigate why this is needed.
        for _ in 0..8 {
            let tx = tx.clone();
            let jobs_rx = jobs_rx.clone();
            let xform = xform.clone();
            thread::spawn(move || {
                for e in jobs_rx {
                    tx.send(xform(e));
                }
            });
        }
        rx
    };
    GatherIter(results_rx.iter())
}<commit_msg>Factory for xform to allow precalculated environment.<commit_after>#![deny(warnings)]

extern crate chan;
extern crate crypto;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use walkdir::WalkDir;
use std::thread;
use std::sync::Arc;
use walkdir::DirEntry;

fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

type Result<T> = std::result::Result<T, TreesumError>;

#[derive(Debug)]
enum TreesumError {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

fn process_root(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let producer_ctor = || WalkDir::new(pb);
    let xform_ctor = || {
        let mut hasher = Sha1::new();
        let mut buf = [0; 1024 * 8];
        let f = move |e: walkdir::Result<DirEntry>| -> Result<String> {
            let e = e.map_err(TreesumError::WalkDir)?;
            if !e.file_type().is_file() {
                return Err(TreesumError::Ignored(e));
            }
            calc_hash(e.path(), &mut hasher, &mut buf).map_err(TreesumError::Io)
        };
        f
    };
    let results = scatter_gather(producer_ctor, xform_ctor);
    for r in results {
        println!("{:?}", r);
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    process_root(root).expect("Hello error 2!");
}

/// The purpose of this newtype is to hide the channel implementation.
struct GatherIter<T>(chan::Iter<T>);
impl<T> Iterator for GatherIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        self.0.next()
    }
}

/// producer_ctor and xform_xtor is needed to allow construction
/// in the correct producer / worker thread.
fn scatter_gather<PC, XC, P, X, J, R>(producer_ctor: PC, xform_ctor: XC) -> GatherIter<R>
    where PC: 'static + std::marker::Send + FnOnce() -> P,
          XC: 'static + std::marker::Send + std::marker::Sync + Fn() -> X,
          X: FnMut(J) -> R,
          J: 'static + std::marker::Send,
          R: 'static + std::marker::Send,
          P: IntoIterator<Item = J>
{
    let jobs_rx = {
        let (tx, rx) = chan::sync(0);
        thread::spawn(move || {
            for e in producer_ctor().into_iter() {
                tx.send(e);
            }
        });
        rx
    };
    let results_rx = {
        let (tx, rx) = chan::sync(0);
        let xform_ctor = Arc::new(xform_ctor); // TODO: Investigate why this is needed.
        for _ in 0..8 {
            let tx = tx.clone();
            let jobs_rx = jobs_rx.clone();
            let xform_ctor = xform_ctor.clone();
            thread::spawn(move || {
                let mut xform = xform_ctor();
                for e in jobs_rx {
                    tx.send(xform(e));
                }
            });
        }
        rx
    };
    GatherIter(results_rx.iter())
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate "rust-md2" as md2;

use md2::{S, S2, md2_compress};
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
use std::slice::bytes::copy_memory;

type Collision = Vec<Vec<u8>>;
type Collisions = Vec<Collision>;

fn find_collisions(state: [[u8, ..49], ..19], k: uint) -> Collisions {
    let mut state = state;
    let mut bytes = Vec::from_elem(k, 0u8);
    let mut collisions: HashMap<Vec<u8>,Collision> = HashMap::new();

    let rows = 16 - k;

    loop {
        copy_memory(state[rows].slice_mut(17, 17 + k), bytes.as_slice());
        copy_memory(state[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

        for row in range(rows + 1, 18) {
            // Fill row.
            for i in range(1, 49) {
                state[row][i] = S[state[row][i - 1] as uint] ^ state[row - 1][i];
            }

            // Next t value.
            state[row + 1][0] = state[row][48] + (row as u8) - 1;
        }

        let key = Vec::from_fn(17 - rows, |row| state[rows + 2 + row][0]);

        match collisions.entry(key) {
            Vacant(entry) => { entry.set(vec!(bytes.clone())); },
            Occupied(mut entry) => { entry.get_mut().push(bytes.clone()); }
        };

        if !increase(bytes.as_mut_slice()) {
            break;
        }
    }

    // Compute original messages for each collision.
    collisions.values().filter(|x| x.len() > 1).map(|collision| {
        collision.iter().map(|bytes| {
            copy_memory(state[rows].slice_mut(17, 17 + k), bytes.as_slice());
            copy_memory(state[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

            // Fill upper rectangles.
            for row in range(1, rows + 1).rev() {
                for col in range(17, 32 - row + 2) {
                    state[row - 1][col] = S[state[row][col - 1] as uint] ^ state[row][col];
                }
            }

            state[0].slice(17, 33).to_vec()
        }).collect()
    }).collect()
}

fn create_initial_state(k: uint) -> [[u8, ..49], ..19] {
    let rows = 16 - k;
    let mut values = [[0u8, ..49], ..19];

    for row in range(1, rows + 1) {
        // Fill row of T1.
        for i in range(1, 17) {
            values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
        }

        // Last bytes are equal.
        values[row][32] = values[row][16];
        values[row][48] = values[row][16];

        // Next t value.
        values[row + 1][0] = values[row][48] + (row as u8) - 1;
    }

    // Compute triangles.
    for col in range(0, rows) {
        for row in range(2 + col, rows + 1).rev() {
            values[row][32 - col - 1] = S2[(values[row][32 - col] ^ values[row - 1][32 - col]) as uint];
            values[row][48 - col - 1] = S2[(values[row][48 - col] ^ values[row - 1][48 - col]) as uint];
        }
    }

    values
}

fn increase(num: &mut [u8]) -> bool {
    let len = num.len();

    // Iterate bytes starting from the end.
    for i in range(0, len).rev() {
        if num[i] == 255 {
            continue;
        }

        // Increase.
        num[i] += 1;

        // Set all bytes right of the current index to zero.
        for i in range(i + 1, len) {
            num[i] = 0;
        }

        return true;
    }

    // Increasing any further would overflow.
    false
}

fn check_collisions(collisions: &Collisions) -> bool {
    let empty = [0u8, ..16];

    for collision in collisions.iter() {
        let mut first_hash: Option<[u8, ..16]> = None;

        for msg in collision.iter() {
            let md2 = md2_compress(&empty, msg.as_slice());

            match first_hash {
                Some(hash) => {
                    if hash != md2 {
                        return false;
                    }
                }
                None => first_hash = Some(md2)
            };
        }
    }

    true
}

fn main() {
    let k = 2;
    let state = create_initial_state(k);
    let collisions = find_collisions(state.clone(), k);
    if !check_collisions(&collisions) {
        panic!("invalid collision found :(");
    }

    println!("Found {} collisions.", collisions.iter().count());
}
<commit_msg>simplify collision checking<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate "rust-md2" as md2;

use md2::{S, S2, md2_compress};
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
use std::slice::bytes::copy_memory;

type Collision = Vec<Vec<u8>>;
type Collisions = Vec<Collision>;

fn find_collisions(state: [[u8, ..49], ..19], k: uint) -> Collisions {
    let mut state = state;
    let mut bytes = Vec::from_elem(k, 0u8);
    let mut collisions: HashMap<Vec<u8>,Collision> = HashMap::new();

    let rows = 16 - k;

    loop {
        copy_memory(state[rows].slice_mut(17, 17 + k), bytes.as_slice());
        copy_memory(state[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

        for row in range(rows + 1, 18) {
            // Fill row.
            for i in range(1, 49) {
                state[row][i] = S[state[row][i - 1] as uint] ^ state[row - 1][i];
            }

            // Next t value.
            state[row + 1][0] = state[row][48] + (row as u8) - 1;
        }

        let key = Vec::from_fn(17 - rows, |row| state[rows + 2 + row][0]);

        match collisions.entry(key) {
            Vacant(entry) => { entry.set(vec!(bytes.clone())); },
            Occupied(mut entry) => { entry.get_mut().push(bytes.clone()); }
        };

        if !increase(bytes.as_mut_slice()) {
            break;
        }
    }

    // Compute original messages for each collision.
    collisions.values().filter(|x| x.len() > 1).map(|collision| {
        collision.iter().map(|bytes| {
            copy_memory(state[rows].slice_mut(17, 17 + k), bytes.as_slice());
            copy_memory(state[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

            // Fill upper rectangles.
            for row in range(1, rows + 1).rev() {
                for col in range(17, 32 - row + 2) {
                    state[row - 1][col] = S[state[row][col - 1] as uint] ^ state[row][col];
                }
            }

            state[0].slice(17, 33).to_vec()
        }).collect()
    }).collect()
}

fn create_initial_state(k: uint) -> [[u8, ..49], ..19] {
    let rows = 16 - k;
    let mut values = [[0u8, ..49], ..19];

    for row in range(1, rows + 1) {
        // Fill row of T1.
        for i in range(1, 17) {
            values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
        }

        // Last bytes are equal.
        values[row][32] = values[row][16];
        values[row][48] = values[row][16];

        // Next t value.
        values[row + 1][0] = values[row][48] + (row as u8) - 1;
    }

    // Compute triangles.
    for col in range(0, rows) {
        for row in range(2 + col, rows + 1).rev() {
            values[row][32 - col - 1] = S2[(values[row][32 - col] ^ values[row - 1][32 - col]) as uint];
            values[row][48 - col - 1] = S2[(values[row][48 - col] ^ values[row - 1][48 - col]) as uint];
        }
    }

    values
}

fn increase(num: &mut [u8]) -> bool {
    let len = num.len();

    // Iterate bytes starting from the end.
    for i in range(0, len).rev() {
        if num[i] == 255 {
            continue;
        }

        // Increase.
        num[i] += 1;

        // Set all bytes right of the current index to zero.
        for i in range(i + 1, len) {
            num[i] = 0;
        }

        return true;
    }

    // Increasing any further would overflow.
    false
}

fn check_collision(collision: &Collision) -> bool {
    let empty = [0u8, ..16];
    let mut first_hash: Option<[u8, ..16]> = None;
    let mut hashes = collision.iter().map(|msg| {
        md2_compress(&empty, msg.as_slice())
    });

    hashes.all(|md2| {
        first_hash.map_or_else(|| {
            first_hash = Some(md2);
            true
        }, |v| v == md2)
    })
}

fn main() {
    let k = 2;
    let state = create_initial_state(k);
    let collisions = find_collisions(state, k);

    // Check that the colliding messages we found generate the same hashes.
    if !collisions.iter().all(check_collision) {
        panic!("invalid collision found :(");
    }

    println!("Found {} collisions.", collisions.iter().count());
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>Proof of concept month<commit_after>fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let yoff = 0;
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();

    for dd in 0..dpw {
        let mut abbrd = String::from(dow[dd]);
        abbrd.truncate(2);
        print!("{} ", abbrd);
    }

    for day in 1 .. dpm + 1 {
        if (day + yoff) % dpw == 1 {
            println!("");
        }

        print!("{:2} ", day);
    }

    println!("");
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>feat: initial structure with scoped threads<commit_after>use std::thread;

fn main() {
    const NTHREADS: i32 = 4;

    thread::scope(|s| {
        for i in 0..NTHREADS {
            s.spawn(move || {
                println!("starting worker {}", i);
            });
        }
        println!("starting job scheduler");
    });

    println!("done");
}

<|endoftext|>