<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate getopts;
extern crate daemonize;
extern crate udt;
extern crate byteorder;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

use daemonize::{Daemonize};
use std::process::Command;
use std::net::{UdpSocket, SocketAddr, SocketAddrV4};
use std::str;
use std::env;
use std::fs::File;
use std::str::FromStr;
use std::path::Path;
use std::ffi::OsStr;
use std::io::{Cursor, Error, Seek, SeekFrom, ErrorKind, stderr, Read, Write};
use getopts::Options;
use udt::*;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{NONCEBYTES, Key, Nonce};
use rustc_serialize::hex::{FromHex, ToHex};

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options] REMOTE-LOCATION", program);
    print!("{}", opts.usage(&brief));
}

fn send_file(stream: UdtSocket, filename: &str, key: Key) -> Result<(), Error> {
    let mut f = File::open(filename).unwrap();

    let mut filesize = 0u64;
    let mut buf = vec![0; 1024 * 1024];
    loop {
        match try!(f.read(&mut buf)) {
            0    => { break },
            read => { filesize += read as u64 },
        }
    }

    let mut wtr = vec![];
    wtr.write_u64::<LittleEndian>(filesize).unwrap();
    match stream.sendmsg(&wtr[..]) {
        Ok(0) => {
            return Err(Error::new(ErrorKind::WriteZero, "failed to write filesize header before timeout"))
        },
        Err(e) => {
            return Err(Error::new(ErrorKind::Other, format!("{:?}", e)))
        }
        _ => {}
    }
    let mut payload = vec![0; 1300];
    f.seek(SeekFrom::Start(0)).unwrap();
    loop {
        match f.read(&mut payload) {
            Ok(0) => {
                break;
            }
            Ok(read) => {
                let nonce = secretbox::gen_nonce();
                let Nonce(noncebytes) = nonce;
                let mut hdr = vec![0u8; 1 + NONCEBYTES];
                hdr[0] = NONCEBYTES as u8;
                hdr[1..].clone_from_slice(&noncebytes);

                let mut sealed = secretbox::seal(&payload[0..read], &nonce, &key);
                let mut msg = Vec::with_capacity(hdr.len() + sealed.len());
                msg.extend_from_slice(&hdr);
                msg.append(&mut sealed);
                match stream.sendmsg(&msg[..]) {
                    Ok(_) =>  { }
                    Err(e) => {
                        stream.close().expect("Error closing stream");
                        panic!("{:?}", e);
                    }
                }
            },
            Err(e) => {
                stream.close().expect("Error closing stream");
                panic!("{:?}", e);
            }
        }
    }

    stream.close().expect("Error closing stream.");
    Ok(())
}

fn get_open_port(start: u16, end: u16) -> Result<u16, ()> {
    assert!(end >= start);
    let mut p = start;
    loop {
        match UdpSocket::bind(&format!("0.0.0.0:{}", p)[..]) {
            Ok(_) => {
                return Ok(p);
            }
            Err(_) => {
                p += 1;
                if p > end {
                    return Err(());
                }
            }
        }
    }
}

fn recv_file(sock: UdtSocket, filesize: u64, filename: &str, key: Key) -> Result<(), Error> {
    let mut f = File::create(filename).unwrap();
    let mut ts = time::precise_time_ns();
    let mut total = 0u64;
    loop {
        let buf = try!(sock.recvmsg(8192).map_err(|e| Error::new(ErrorKind::Other, format!("{:?}", e))));
        if buf.len() < 1 {
            return Err(Error::new(ErrorKind::InvalidInput, "empty message"));
        }
        let noncelen = buf[0] as usize;
        if noncelen != NONCEBYTES {
            return Err(Error::new(ErrorKind::InvalidInput, "nonce bytes unexpected len"));
        }
        if buf.len() < (1 + noncelen) {
            return Err(Error::new(ErrorKind::InvalidInput, "nonce != nonce_len"));
        }
        let mut noncebytes = [0u8; NONCEBYTES];
        noncebytes.copy_from_slice(&buf[1..1+noncelen]);
        let nonce = Nonce(noncebytes);

        let unsealed = try!(secretbox::open(&buf[1+noncelen..], &nonce, &key).map_err(|_| Error::new(ErrorKind::InvalidInput, "failed to decrypt")));

        total += unsealed.len() as u64;
        f.write_all(&unsealed[..]).unwrap();
        if time::precise_time_ns() > ts + 10000000 {
            print!("\rreceived {:.1}M / {:.1}M ({:.1}%)                      ", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64);
            ts = time::precise_time_ns();
        }
        if total >= filesize {
            println!("\nEOF");
            break;
        }
    }
    let _ = sock.close();
    Ok(())
}

fn main() {
    enum Mode {Server, Client}
    env_logger::init().expect("Error starting logger");

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    // opts.optopt("o", "output", "set output file name", "NAME");
    opts.optflag("s", "server", "server mode");
    opts.optflag("p", "port-range", "server listening port range");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let mode: Mode = match matches.opt_present("s") {
        true => Mode::Server,
        false => Mode::Client
    };

    match mode {
        Mode::Server => {
            let sshconnstr = env::var("SSH_CONNECTION").unwrap_or(String::from("0.0.0.0 0 0.0.0.0 22")).trim().to_owned();
            let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
            let ip = sshconn[2];
            let port = get_open_port(55000, 55100).unwrap();
            let key = secretbox::gen_key();
            match key {
                Key(keybytes) => { println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex()) }
            }

            let daemonize = Daemonize::new();
            match daemonize.start() {
                Ok(_) => { let _ = writeln!(&mut stderr(), "daemonized"); }
                Err(_) => { let _ = writeln!(&mut stderr(), "RWRWARWARARRR"); }
            }

            udt::init();
            let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Datagram).unwrap();
            sock.setsockopt(UdtOpts::UDP_RCVBUF, 1024000i32).unwrap();
            sock.setsockopt(UdtOpts::UDP_SNDBUF, 1024000i32).unwrap();
            sock.bind(SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap())).unwrap();

            sock.listen(1).unwrap();

            let (stream, _) = sock.accept().unwrap();

            if let Ok(version) = stream.recvmsg(1) {
                if version[0] == 0x00 {
                    send_file(stream, &input, key).unwrap();
                } else {
                    panic!("Unrecognized version.");
                }
            } else {
                panic!("Failed to receive version byte from client.");
            }
        }
        Mode::Client => {
            let sections: Vec<&str> = input.split(":").collect();
            let addr: String = sections[0].to_owned();
            let path: String = sections[1].to_owned();
            let cmd = format!("shoop -s {}", path);
            println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

            let output = Command::new("ssh")
                                 .arg(addr.to_owned())
                                 .arg(cmd)
                                 .output()
                                 .unwrap_or_else(|e| {
                                     panic!("failed to execute process: {}", e);
                                 });
            let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
            let info: Vec<&str> = infostring.split(" ").collect();
            if info.len() != 5 {
                panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
            }

            let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
            println!("connecting to {}:{}", ip, port);
            if magic != "shoop" || version != "0" {
                panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
            }

            let mut keybytes = [0u8; 32];
            keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
            let key = Key(keybytes);

            udt::init();
            let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Datagram).unwrap();
            sock.setsockopt(UdtOpts::UDP_RCVBUF, 1024000i32).unwrap();
            sock.setsockopt(UdtOpts::UDP_SNDBUF, 1024000i32).unwrap();
            let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
            match sock.connect(addr) {
               Ok(()) => {
                   println!("connected!");
               },
               Err(e) => {
                   panic!("errrrrrrr {:?}", e);
               }
            }

            sock.sendmsg(&[0u8; 1]).unwrap();
            // println!("checking if server is frand");

            match sock.recvmsg(8) {
               Ok(msg) => {
                   if msg.len() == 0 {
                       panic!("failed to get filesize from server, probable timeout.");
                   }
                   let mut rdr = Cursor::new(msg);
                   let filesize = rdr.read_u64::<LittleEndian>().unwrap();
                   let filename = Path::new(&path).file_name().unwrap_or(OsStr::new("outfile")).to_str().unwrap_or("outfile");
                   println!("{}, {:.1}MB", filename, (filesize as f64)/(1024f64*1024f64));
                   recv_file(sock, filesize, filename, key).unwrap();
               }
               Err(e) => {
                   panic!("{:?}", e);
               }
            }
        }
    }
}

<commit_msg>allow files with spaces in names.<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate getopts;
extern crate daemonize;
extern crate udt;
extern crate byteorder;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

use daemonize::{Daemonize};
use std::process::Command;
use std::net::{UdpSocket, SocketAddr, SocketAddrV4};
use std::str;
use std::env;
use std::fs::File;
use std::str::FromStr;
use std::path::Path;
use std::ffi::OsStr;
use std::io::{Cursor, Error, Seek, SeekFrom, ErrorKind, stderr, Read, Write};
use getopts::Options;
use udt::*;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{NONCEBYTES, Key, Nonce};
use rustc_serialize::hex::{FromHex, ToHex};

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options] REMOTE-LOCATION", program);
    print!("{}", opts.usage(&brief));
}

fn send_file(stream: UdtSocket, filename: &str, key: Key) -> Result<(), Error> {
    let mut f = File::open(filename).unwrap();

    let mut filesize = 0u64;
    let mut buf = vec![0; 1024 * 1024];
    loop {
        match try!(f.read(&mut buf)) {
            0    => { break },
            read => { filesize += read as u64 },
        }
    }

    let mut wtr = vec![];
    wtr.write_u64::<LittleEndian>(filesize).unwrap();
    match stream.sendmsg(&wtr[..]) {
        Ok(0) => {
            return Err(Error::new(ErrorKind::WriteZero, "failed to write filesize header before timeout"))
        },
        Err(e) => {
            return Err(Error::new(ErrorKind::Other, format!("{:?}", e)))
        }
        _ => {}
    }
    let mut payload = vec![0; 1300];
    f.seek(SeekFrom::Start(0)).unwrap();
    loop {
        match f.read(&mut payload) {
            Ok(0) => {
                break;
            }
            Ok(read) => {
                let nonce = secretbox::gen_nonce();
                let Nonce(noncebytes) = nonce;
                let mut hdr = vec![0u8; 1 + NONCEBYTES];
                hdr[0] = NONCEBYTES as u8;
                hdr[1..].clone_from_slice(&noncebytes);

                let mut sealed = secretbox::seal(&payload[0..read], &nonce, &key);
                let mut msg = Vec::with_capacity(hdr.len() + sealed.len());
                msg.extend_from_slice(&hdr);
                msg.append(&mut sealed);
                match stream.sendmsg(&msg[..]) {
                    Ok(_) =>  { }
                    Err(e) => {
                        stream.close().expect("Error closing stream");
                        panic!("{:?}", e);
                    }
                }
            },
            Err(e) => {
                stream.close().expect("Error closing stream");
                panic!("{:?}", e);
            }
        }
    }

    stream.close().expect("Error closing stream.");
    Ok(())
}

fn get_open_port(start: u16, end: u16) -> Result<u16, ()> {
    assert!(end >= start);
    let mut p = start;
    loop {
        match UdpSocket::bind(&format!("0.0.0.0:{}", p)[..]) {
            Ok(_) => {
                return Ok(p);
            }
            Err(_) => {
                p += 1;
                if p > end {
                    return Err(());
                }
            }
        }
    }
}

fn recv_file(sock: UdtSocket, filesize: u64, filename: &str, key: Key) -> Result<(), Error> {
    let mut f = File::create(filename).unwrap();
    let mut ts = time::precise_time_ns();
    let mut total = 0u64;
    loop {
        let buf = try!(sock.recvmsg(8192).map_err(|e| Error::new(ErrorKind::Other, format!("{:?}", e))));
        if buf.len() < 1 {
            return Err(Error::new(ErrorKind::InvalidInput, "empty message"));
        }
        let noncelen = buf[0] as usize;
        if noncelen != NONCEBYTES {
            return Err(Error::new(ErrorKind::InvalidInput, "nonce bytes unexpected len"));
        }
        if buf.len() < (1 + noncelen) {
            return Err(Error::new(ErrorKind::InvalidInput, "nonce != nonce_len"));
        }
        let mut noncebytes = [0u8; NONCEBYTES];
        noncebytes.copy_from_slice(&buf[1..1+noncelen]);
        let nonce = Nonce(noncebytes);

        let unsealed = try!(secretbox::open(&buf[1+noncelen..], &nonce, &key).map_err(|_| Error::new(ErrorKind::InvalidInput, "failed to decrypt")));

        total += unsealed.len() as u64;
        f.write_all(&unsealed[..]).unwrap();
        if time::precise_time_ns() > ts + 10000000 {
            print!("\rreceived {:.1}M / {:.1}M ({:.1}%)                      ", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64);
            ts = time::precise_time_ns();
        }
        if total >= filesize {
            println!("\nEOF");
            break;
        }
    }
    let _ = sock.close();
    Ok(())
}

fn main() {
    enum Mode {Server, Client}
    env_logger::init().expect("Error starting logger");

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    // opts.optopt("o", "output", "set output file name", "NAME");
    opts.optflag("s", "server", "server mode");
    opts.optflag("p", "port-range", "server listening port range");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let mode: Mode = match matches.opt_present("s") {
        true => Mode::Server,
        false => Mode::Client
    };

    match mode {
        Mode::Server => {
            let sshconnstr = env::var("SSH_CONNECTION").unwrap_or(String::from("0.0.0.0 0 0.0.0.0 22")).trim().to_owned();
            let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
            let ip = sshconn[2];
            let port = get_open_port(55000, 55100).unwrap();
            let key = secretbox::gen_key();
            match key {
                Key(keybytes) => { println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex()) }
            }

            let daemonize = Daemonize::new();
            match daemonize.start() {
                Ok(_) => { let _ = writeln!(&mut stderr(), "daemonized"); }
                Err(_) => { let _ = writeln!(&mut stderr(), "RWRWARWARARRR"); }
            }

            udt::init();
            let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Datagram).unwrap();
            sock.setsockopt(UdtOpts::UDP_RCVBUF, 1024000i32).unwrap();
            sock.setsockopt(UdtOpts::UDP_SNDBUF, 1024000i32).unwrap();
            sock.bind(SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap())).unwrap();

            sock.listen(1).unwrap();

            let (stream, _) = sock.accept().unwrap();

            if let Ok(version) = stream.recvmsg(1) {
                if version[0] == 0x00 {
                    send_file(stream, &input, key).unwrap();
                } else {
                    panic!("Unrecognized version.");
                }
            } else {
                panic!("Failed to receive version byte from client.");
            }
        }
        Mode::Client => {
            let sections: Vec<&str> = input.split(":").collect();
            let addr: String = sections[0].to_owned();
            let path: String = sections[1].to_owned();
            let cmd = format!("shoop -s '{}'", path);
            println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

            let output = Command::new("ssh")
                                 .arg(addr.to_owned())
                                 .arg(cmd)
                                 .output()
                                 .unwrap_or_else(|e| {
                                     panic!("failed to execute process: {}", e);
                                 });
            let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
            let info: Vec<&str> = infostring.split(" ").collect();
            if info.len() != 5 {
                panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
            }

            let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
            println!("connecting to {}:{}", ip, port);
            if magic != "shoop" || version != "0" {
                panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
            }

            let mut keybytes = [0u8; 32];
            keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
            let key = Key(keybytes);

            udt::init();
            let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Datagram).unwrap();
            sock.setsockopt(UdtOpts::UDP_RCVBUF, 1024000i32).unwrap();
            sock.setsockopt(UdtOpts::UDP_SNDBUF, 1024000i32).unwrap();
            let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
            match sock.connect(addr) {
               Ok(()) => {
                   println!("connected!");
               },
               Err(e) => {
                   panic!("errrrrrrr {:?}", e);
               }
            }

            sock.sendmsg(&[0u8; 1]).unwrap();
            // println!("checking if server is frand");

            match sock.recvmsg(8) {
               Ok(msg) => {
                   if msg.len() == 0 {
                       panic!("failed to get filesize from server, probable timeout.");
                   }
                   let mut rdr = Cursor::new(msg);
                   let filesize = rdr.read_u64::<LittleEndian>().unwrap();
                   let filename = Path::new(&path).file_name().unwrap_or(OsStr::new("outfile")).to_str().unwrap_or("outfile");
                   println!("{}, {:.1}MB", filename, (filesize as f64)/(1024f64*1024f64));
                   recv_file(sock, filesize, filename, key).unwrap();
               }
               Err(e) => {
                   panic!("{:?}", e);
               }
            }
        }
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate regex;
extern crate xdg_basedir;

use std::collections;
use std::fs;
use std::io;
use std::io::BufRead;
use std::path;

fn get_default_config_directory() -> String {
    let mut config_home_dir = xdg_basedir::get_config_home()
        .ok()
        .expect("Unable to detect default q directory.");

    config_home_dir.push("q");
    config_home_dir.push("rules");

    match config_home_dir.to_str() {
        Some(content) => return content.to_string(),
        None => panic!("Cannot get default config directory!")
    }
}

fn parse_rules_filenames(rules: &str, config_dir: &str) -> collections::HashSet<path::PathBuf> {
    rules
        .split(",")
        .map(
            |item| {
                let mut root_path = path::PathBuf::from(config_dir);
                root_path.push(item);
                root_path
            }
        )
        .collect::<collections::HashSet<path::PathBuf>>()
}

fn parse_rules(filenames: &collections::HashSet<path::PathBuf>, case_insensitive: bool) -> regex::Regex {
    let mut regex_buffer: Vec<String> = Vec::with_capacity(filenames.len() * 2);

    for filename in filenames.iter() {
        let path = filename.as_path();

        let file = fs::File::open(path)
            .ok()
            .expect(
                &format!("Cannot open file {:?}.", path)
            );

        for line in io::BufReader::new(file).lines() {
            let content = line
                .ok()
                .expect(
                    &format!("Cannot fetch a line from file {:?}.", path)
                );
            let quoted_content = &regex::quote(&content);
            regex_buffer.push(
                if case_insensitive {
                    format!("(?i{})", quoted_content)
                } else {
                    format!("({})", quoted_content)
                }
            );
        }
    }

    regex::Regex::new(&regex_buffer.connect("|"))
        .ok()
        .expect("Cannot compile regexps")
}

fn main() {
    let options = clap::App::new("q")
        .author("Sergey Arkhipov <nineseconds@yandex.ru>")
        .version(&crate_version!()[..])
        .about("q is a gentle way to grep using predefined regexp sets.")
        .after_help("Please find more documentation at https://github.com/9seconds/q.")
        .arg(
            clap::Arg::with_name("SAME_LINE")
                .help("Keep matches on the same line")
                .short("l")
                .long("same_line")
        ).arg(
            clap::Arg::with_name("CASE_INSENSITIVE")
                .help("Use case insensitive regex versions.")
                .short("i")
                .long("case-insensitive")
        ).arg(
            clap::Arg::with_name("RULES_DIRECTORY")
                .help("Directory where rules could be found. By default it uses $XDG_CONFIG_HOME/q/rules")
                .short("-r")
                .long("rules")
                .takes_value(true)
        ).arg(
            clap::Arg::with_name("FILE")
                .help("File to process. Use '-' to read from stdin (default is stdin).")
                .short("-f")
                .long("file")
                .takes_value(true)
        ).arg(
            clap::Arg::with_name("RULES")
                .help("Regexp rules to apply to the stdin as a comma-separated list.")
                .index(1)
                .required(true)
        )
        .get_matches();

    let same_line = options.is_present("SAME_LINE");
    let case_insensitive = options.is_present("CASE_INSENSITIVE");

    let default_config_directory = get_default_config_directory();
    let rules_directory = options
        .value_of("RULES_DIRECTORY")
        .unwrap_or(&default_config_directory);

    let filename = options
        .value_of("FILE")
        .unwrap_or("-");
    let rules_filenames = parse_rules_filenames(
        options.value_of("RULES").unwrap(),
        rules_directory
    );
    let rules = parse_rules(&rules_filenames, case_insensitive);

    println!("Options: {}, {}, {}, {:?}", same_line, rules_directory, filename, rules);
}
<commit_msg>Small internal refactoring<commit_after>#[macro_use]
extern crate clap;
extern crate regex;
extern crate xdg_basedir;

use std::collections;
use std::fs;
use std::io;
use std::io::BufRead;
use std::path;
use std::path::PathBuf;

fn get_rules_directory(options: Option<&str>) -> Result<PathBuf, String> {
    if let Some(directory) = options {
        Ok(PathBuf::from(directory))
    } else {
        let mut config_home_dir = try!(
            xdg_basedir::get_config_home().map_err(|e| e.to_string())
        );

        config_home_dir.push("q");
        config_home_dir.push("rules");

        Ok(config_home_dir)
    }
}

fn parse_rules_filenames(rules: &str, config_dir: &PathBuf) -> collections::HashSet<path::PathBuf> {
    rules
        .split(",")
        .map(
            |item| {
                let mut root_path = config_dir.clone();
                root_path.push(item);
                root_path
            }
        )
        .collect::<collections::HashSet<path::PathBuf>>()
}

fn parse_rules(filenames: &collections::HashSet<path::PathBuf>, case_insensitive: bool) -> Result<regex::Regex, String> {
    let mut regex_buffer: Vec<String> = Vec::with_capacity(filenames.len() * 2);

    for filename in filenames.iter() {
        let path = filename.as_path();
        let file = try!(fs::File::open(path).map_err(|e| e.to_string()));
        let reader = io::BufReader::new(file);

        for line in reader.lines() {
            match line {
                Ok(content) => {
                    let quoted_content = &regex::quote(&content);
                    regex_buffer.push(
                        if case_insensitive {
                            format!("(?i{})", quoted_content)
                        } else {
                            format!("({})", quoted_content)
                        }
                    )
                },
                Err(error) => return Err(
                    format!(
                        "Cannot fetch a line from file {}: {}!",
                        path.display(),
                        error.to_string()
                    )
                )
            }
        }
    }

    let compiled_result = regex::Regex::new(&regex_buffer.connect("|"));
    if let Err(e) = compiled_result {
        return Err(format!("Cannot compile regexps: {}", e.to_string()))
    }

    Ok(compiled_result.ok().unwrap())
}

fn get_rules(rules_directory: &PathBuf, rules_str: &str, case_insensitive: bool) -> Result<regex::Regex, String> {
    let filenames = parse_rules_filenames(rules_str, rules_directory);
    parse_rules(&filenames, case_insensitive)
}

fn main() {
    let options = clap::App::new("q")
        .author("Sergey Arkhipov <nineseconds@yandex.ru>")
        .version(&crate_version!()[..])
        .about("q is a gentle way to grep using predefined regexp sets.")
        .after_help("Please find more documentation at https://github.com/9seconds/q.")
        .arg(
            clap::Arg::with_name("SAME_LINE")
                .help("Keep matches on the same line")
                .short("l")
                .long("same_line")
        ).arg(
            clap::Arg::with_name("CASE_INSENSITIVE")
                .help("Use case insensitive regex versions.")
                .short("i")
                .long("case-insensitive")
        ).arg(
            clap::Arg::with_name("RULES_DIRECTORY")
                .help("Directory where rules could be found. By default it uses $XDG_CONFIG_HOME/q/rules")
                .short("-r")
                .long("rules")
                .takes_value(true)
        ).arg(
            clap::Arg::with_name("FILE")
                .help("File to process. Use '-' to read from stdin (default is stdin).")
                .short("-f")
                .long("file")
                .takes_value(true)
        ).arg(
            clap::Arg::with_name("RULES")
                .help("Regexp rules to apply to the stdin as a comma-separated list.")
                .index(1)
                .required(true)
        )
        .get_matches();

    let same_line = options.is_present("SAME_LINE");
    let case_insensitive = options.is_present("CASE_INSENSITIVE");
    let rules_directory = get_rules_directory(options.value_of("RULES_DIRECTORY"))
        .ok()
        .expect("Cannot determine rules directory!");
    let filename = options
        .value_of("FILE")
        .unwrap_or("-");

    let rules: regex::Regex;
    match get_rules(&rules_directory, options.value_of("RULES").unwrap(), case_insensitive) {
        Ok(result) => rules = result,
        Err(error) => panic!("Cannot parse rules: {}", error)
    }

    println!("Options: {}, {}, {}, {:?}", same_line, case_insensitive, filename, rules);
}
<|endoftext|>


<commit_before>extern crate goga;

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

    goga::server::start_server::run();
}
<commit_msg>Oh hell no, is Hello, Goga!<commit_after>extern crate goga;

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

    goga::server::start_server::run();
}
<|endoftext|>


<commit_before>mod cartridge;
mod cpu;
mod disasm;
mod interrupt;
mod mem;
mod ram;
mod serial;
mod sound;
mod timer;
mod video;

//
// Memory Map
//

struct Dummy;
impl mem::Mem for Dummy {
  fn loadb(&mut self, addr: u16) -> u8 {
    warn!("load in unmapped memory at 0x{:04X}", addr);
    0xff
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    warn!("store in unmapped memory at 0x{:04X}", addr);
  }
}

struct MemMap {
  cart: ~cartridge::Cartridge,
  wram: ram::WorkRam,
  timer: timer::Timer,
  intr: interrupt::InterruptCtrl,
  sound: sound::Sound,
  video: video::Video,
  serial: serial::SerialIO,
  dummy: Dummy,
}

impl MemMap {
  fn mem_from_addr<'a>(&'a mut self, addr: u16) -> &'a mut mem::Mem {
    match addr {
      0x0000..0x7fff | // ROM banks
      0xa000..0xbfff   // External RAM
                      => &mut *self.cart as &mut mem::Mem,
      0x8000..0x9fff | // VRAM
      0xfe00..0xfe9f | // OAM
      0xff40..0xff4b   // Video I/O
                      => &mut self.video as &mut mem::Mem,
      0xc000..0xfdff | // WRAM (including echo area 0xe000-0xfdff)
      0xff80..0xfffe   // HRAM
                      => &mut self.wram as &mut mem::Mem,
      0xff01..0xff02  => &mut self.serial as &mut mem::Mem,
      0xff04..0xff07  => &mut self.timer as &mut mem::Mem,
      0xff0f | 0xffff => &mut self.intr as &mut mem::Mem,
      0xff10..0xff3f  => &mut self.sound as &mut mem::Mem,
      _ => &mut self.dummy as &mut mem::Mem,
      //_ => fail!("unmapped memory at 0x{:04X}", addr),
    }
  }
}

impl mem::Mem for MemMap {
  fn loadb(&mut self, addr: u16) -> u8 {
    self.mem_from_addr(addr).loadb(addr)
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    self.mem_from_addr(addr).storeb(addr, val)
  }
}

fn main() {
  let args = std::os::args();
  if args.len() != 2 {
    println!("Usage: {:s} rom.gb", args[0]);
    return;
  }

  let cart = ~cartridge::Cartridge::from_path(&Path::new(args[1]));
  println!("Name: {:s}", cart.title);
  println!("Type: {:u}", cart.cartridge_type);

  let memmap = MemMap {
    cart: cart,
    wram: ram::WorkRam::new(),
    timer: timer::Timer::new(),
    intr: interrupt::InterruptCtrl::new(),
    sound: sound::Sound,
    video: video::Video,
    serial: serial::SerialIO::new(),
    dummy: Dummy,
  };
  let mut cpu = cpu::Cpu::new(memmap);

  cpu.regs.pc = 0x100;
  loop {
    if log_enabled!(std::logging::DEBUG) {
      debug!("AF={:02X}{:02X} BC={:02X}{:02X} DE={:02X}{:02X} HL={:02X}{:02X} SP={:04X} PC={:04X} CY={:u}",
             cpu.regs.a, cpu.regs.f,
             cpu.regs.b, cpu.regs.c,
             cpu.regs.d, cpu.regs.e,
             cpu.regs.h, cpu.regs.l,
             cpu.regs.sp,
             cpu.regs.pc,
             cpu.cycles);
      let mut d = disasm::Disasm { mem: &mut cpu.mem, pc: cpu.regs.pc };
      debug!("{:s}", cpu::decode(&mut d));
    }
    let cycles = cpu.step();
    cpu.mem.timer.tick(cycles);
  }
}
<commit_msg>Added crude debugger<commit_after>mod cartridge;
mod cpu;
mod disasm;
mod interrupt;
mod mem;
mod ram;
mod serial;
mod sound;
mod timer;
mod video;

//
// Memory Map
//

struct Dummy;
impl mem::Mem for Dummy {
  fn loadb(&mut self, addr: u16) -> u8 {
    info!("load in unmapped memory at 0x{:04X}", addr);
    0xff
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    info!("store in unmapped memory at 0x{:04X}", addr);
  }
}

struct MemMap {
  cart: ~cartridge::Cartridge,
  wram: ram::WorkRam,
  timer: timer::Timer,
  intr: interrupt::InterruptCtrl,
  sound: sound::Sound,
  video: video::Video,
  serial: serial::SerialIO,
  dummy: Dummy,
}

impl MemMap {
  fn mem_from_addr<'a>(&'a mut self, addr: u16) -> &'a mut mem::Mem {
    match addr {
      0x0000..0x7fff | // ROM banks
      0xa000..0xbfff   // External RAM
                      => &mut *self.cart as &mut mem::Mem,
      0x8000..0x9fff | // VRAM
      0xfe00..0xfe9f | // OAM
      0xff40..0xff4b   // Video I/O
                      => &mut self.video as &mut mem::Mem,
      0xc000..0xfdff | // WRAM (including echo area 0xe000-0xfdff)
      0xff80..0xfffe   // HRAM
                      => &mut self.wram as &mut mem::Mem,
      0xff01..0xff02  => &mut self.serial as &mut mem::Mem,
      0xff04..0xff07  => &mut self.timer as &mut mem::Mem,
      0xff0f | 0xffff => &mut self.intr as &mut mem::Mem,
      0xff10..0xff3f  => &mut self.sound as &mut mem::Mem,
      _ => &mut self.dummy as &mut mem::Mem,
      //_ => fail!("unmapped memory at 0x{:04X}", addr),
    }
  }
}

impl mem::Mem for MemMap {
  fn loadb(&mut self, addr: u16) -> u8 {
    self.mem_from_addr(addr).loadb(addr)
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    self.mem_from_addr(addr).storeb(addr, val)
  }
}

fn disasm<M: mem::Mem>(mem: &mut M, addr: &mut u16) -> ~str {
  let mut d = disasm::Disasm { mem: mem, pc: *addr };
  let result = cpu::decode(&mut d);
  *addr = d.pc;
  result
}

fn disassemble<M: mem::Mem>(mem: &mut M, addr: u16) {
  let mut pc = addr;

  for _ in range(0, 5) {
    let start_addr = pc;
    let instr = disasm(mem, &mut pc);
    let end_addr = pc;
    print!("${:04X}", start_addr);
    for a in range(start_addr, end_addr) {
      print!(" {:02X}", mem.loadb(a));
    }
    println!("\t{:s}", instr);
  }
}

fn parse_addr(s: &str) -> Option<u16> {
  let mut slice = s;
  let mut radix = 10;
  if slice.starts_with("$") {
    slice = slice.slice_from(1);
    radix = 16;
  }
  std::num::from_str_radix::<u16>(slice, radix)
}

fn main() {
  let args = std::os::args();
  if args.len() != 2 {
    println!("Usage: {:s} rom.gb", args[0]);
    return;
  }

  let cart = ~cartridge::Cartridge::from_path(&Path::new(args[1]));
  println!("Name: {:s}", cart.title);
  println!("Type: {:u}", cart.cartridge_type);

  let memmap = MemMap {
    cart: cart,
    wram: ram::WorkRam::new(),
    timer: timer::Timer::new(),
    intr: interrupt::InterruptCtrl::new(),
    sound: sound::Sound,
    video: video::Video::new(),
    serial: serial::SerialIO::new(),
    dummy: Dummy,
  };
  let mut cpu = cpu::Cpu::new(memmap);
  cpu.regs.pc = 0x100;

  let mut stdin = std::io::buffered::BufferedReader::new(std::io::stdio::stdin());

  let mut intr_listener = std::io::signal::Listener::new();
  intr_listener.register(std::io::signal::Interrupt);

  let mut done = false;
  let mut running = false;
  let mut breakpoints: ~[u16] = ~[];

  while !done {
    if running {
      match breakpoints.iter().find(|&bp| *bp == cpu.regs.pc) {
        Some(&bp) => {
          println!("Breakpoint at ${:04X}", bp);
          running = false
        },
        None => (),
      }

      match intr_listener.port.try_recv() {
        Some(std::io::signal::Interrupt) => {
          println("Interrupted");
          running = false
        },
        _ => (),
      }
    }

    if !running {
      loop {
        print("> ");
        std::io::stdio::flush();
        match stdin.read_line() {
          Some(line) => {
            let words = line.words().to_owned_vec();
            if words.len() >= 1 {
              let command = words[0];
              match command {
                &"q" => { done = true; break }, // quit
                &"s" => { break }, // step,
                &"r" => { running = true; break }, // run
                &"regs" => { // print registers
                  println!("AF={:02X}{:02X} BC={:02X}{:02X} DE={:02X}{:02X} HL={:02X}{:02X} SP={:04X} PC={:04X} CY={:u}",
                           cpu.regs.a, cpu.regs.f,
                           cpu.regs.b, cpu.regs.c,
                           cpu.regs.d, cpu.regs.e,
                           cpu.regs.h, cpu.regs.l,
                           cpu.regs.sp,
                           cpu.regs.pc,
                           cpu.cycles);
                },
                &"d" => { // disasm
                  let mut addr = cpu.regs.pc;
                  if words.len() >= 2 {
                    match parse_addr(words[1]) {
                      Some(a) => addr = a,
                      None    => error!("Invalid address: {:s}", words[1]),
                    }
                  }
                  disassemble(&mut cpu.mem, addr)
                },
                &"b" => { // breakpoint
                  if words.len() == 1 {
                    // show breakpoints
                    if breakpoints.len() == 0 {
                      println("No breakpoints");
                    } else {
                      println("Breakpoints:");
                      for bp in breakpoints.iter() {
                        println!("  ${:04X}", *bp);
                      }
                    }
                  } else if words.len() >= 2 {
                    // add/remove breakpoint
                    let mut add = true;
                    let mut arg = words[1];
                    if arg.starts_with("-") {
                      arg = arg.slice_from(1);
                      add = false;
                    }
                    match parse_addr(arg) {
                      Some(addr) => {
                        if add {
                          if !breakpoints.contains(&addr) {
                            breakpoints.push(addr);
                            breakpoints.sort();
                            println!("Breakpoint ${:04X} added", addr);
                          } else {

                          }
                        } else {
                          if breakpoints.contains(&addr) {
                            breakpoints.retain(|&bp| bp != addr);
                            println!("Breakpoint ${:04X} removed", addr);
                          } else {
                            error!("No such breakpoint ${:04X}", addr);
                          }
                        }
                      },
                      None => error!("Invalid address: {:s}", words[1]),
                    }
                  }
                },
                _ => error!("Unknown command: {:s}", command),
              }
            }
          },
          None => {
            println("\nType q to exit");
            intr_listener.port.recv();
          }
        }
      }
    }

    let cycles = cpu.step();
    cpu.mem.timer.tick(cycles);
    cpu.mem.video.tick(cycles);
  }
}
<|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/.

#![feature(slice_bytes, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate devicemapper;
#[macro_use]
extern crate clap;
extern crate nix;
extern crate crc;
extern crate byteorder;
extern crate uuid;
extern crate time;
extern crate serde;
extern crate serde_json;

#[allow(unused_imports)]
use std::io;
use std::io::{Read, Write, ErrorKind, Seek, SeekFrom};
use std::error::Error;
use std::process::exit;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::slice::bytes::copy_memory;
use std::os::unix::prelude::AsRawFd;
use std::fmt;

use devicemapper as devm;
use devicemapper::Device;
use clap::{App, Arg, SubCommand, ArgMatches};
use nix::sys::{stat, ioctl};
use crc::crc32;
use byteorder::{LittleEndian, ByteOrder};
use uuid::Uuid;

const SECTOR_SIZE: usize = 512;
const FRO_HEADER_SIZE: usize = 512;
const FRO_MDA_ZONE_SIZE: usize = (1024 * 1024);
const FRO_MAGIC: &'static [u8] = b"!IamFroy0\x86\xffGO\x02^\x41";

// No devs smaller than a gig
const MIN_DEV_SIZE: usize = (1024 * 1024 * 1024);

static mut debug: bool = false;

macro_rules! dbgp {
    ($($arg:tt)*) => (
        unsafe {
            if debug {
                println!($($arg)*)
            }
        })
}

macro_rules! errp {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        })
}

// Define a common error enum.
// See http://blog.burntsushi.net/rust-error-handling/
#[derive(Debug)]
enum FroyoError {
    Io(io::Error),
    Serde(serde_json::error::Error),
}

impl fmt::Display for FroyoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FroyoError::Io(ref err) => write!(f, "IO error: {}", err),
            FroyoError::Serde(ref err) => write!(f, "Serde error: {}", err),
        }
    }
}

impl Error for FroyoError {
    fn description(&self) -> &str {
        match *self {
            FroyoError::Io(ref err) => err.description(),
            FroyoError::Serde(ref err) => Error::description(err),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            FroyoError::Io(ref err) => Some(err),
            FroyoError::Serde(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for FroyoError {
    fn from(err: io::Error) -> FroyoError {
        FroyoError::Io(err)
    }
}

impl From<serde_json::error::Error> for FroyoError {
    fn from(err: serde_json::error::Error) -> FroyoError {
        FroyoError::Serde(err)
    }
}

fn blkdev_size(file: &File) -> io::Result<u64> {
    // BLKGETSIZE64
    let op = ioctl::op_read(0x12, 114, 8);
    let mut val: u64 = 0;

    match unsafe { ioctl::read_into(file.as_raw_fd(), op, &mut val) } {
        Err(_) => return Err((io::Error::last_os_error())),
        Ok(_) => Ok(val),
    }
}

#[derive(Debug, Clone, Copy, Serialize)]
struct FroyoDev {
    dev: Device,
}

impl FroyoDev {
    fn new(dev: Device) -> io::Result<FroyoDev> {
        unimplemented!()
    }

    fn initialize(dev: Device, force: bool) -> io::Result<FroyoDev> {
        let pathbuf = try!(dev.path().ok_or(io::Error::new(
            ErrorKind::NotFound,
            format!("Could not get path for {}:{}", dev.major, dev.minor))));

        let pstat = match stat::stat(&pathbuf) {
            Err(_) => return Err(io::Error::new(
                ErrorKind::NotFound,
                format!("{} not found", &pathbuf.display()))),
            Ok(x) => x,
        };

        if pstat.st_mode & 0x6000 != 0x6000 {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a block device", &pathbuf.display())));
        }

        let mut f = match OpenOptions::new().read(true).write(true).open(&pathbuf) {
            Err(_) => {
                return Err(io::Error::new(
                    ErrorKind::PermissionDenied,
                    format!("Could not open {}", &pathbuf.display())));
            },
            Ok(x) => x,
        };

        if !force {
            let mut buf = [0u8; 4096];
            try!(f.read(&mut buf));

            if buf.iter().any(|x| *x != 0) {
                return Err(io::Error::new(
                    ErrorKind::InvalidInput,
                    format!("First 4K of {} is not zeroed, need to use --force",
                            &pathbuf.display())));
            }
        }

        let dev_size = try!(blkdev_size(&f));
        if dev_size < MIN_DEV_SIZE as u64 {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} too small, 1G minimum", &pathbuf.display())));
        }

        let mut buf = [0u8; FRO_MDA_ZONE_SIZE];

        copy_memory(FRO_MAGIC, &mut buf[4..20]);
        LittleEndian::write_u64(&mut buf[20..28], dev_size / SECTOR_SIZE as u64);
        // no flags
        copy_memory(Uuid::new_v4().to_simple_string().as_bytes(), &mut buf[32..64]);
        // no MDAs in use yet

        // All done, calc CRC and write
        let crc = crc32::checksum_ieee(&buf[4..FRO_HEADER_SIZE]);
        LittleEndian::write_u32(&mut buf[..4], crc);

        try!(f.seek(SeekFrom::Start(0)));
        try!(f.write_all(&buf));
        try!(f.seek(SeekFrom::End(-(FRO_MDA_ZONE_SIZE as i64))));
        try!(f.write_all(&buf));

        try!(f.flush());

        Ok(FroyoDev {dev: dev})
    }
}

#[derive(Debug)]
struct Froyo {
    name: String,
    devs: Vec<FroyoDev>,
}

impl Froyo {
    fn create(name: &str, fds: Vec<FroyoDev>) -> io::Result<Froyo> {
        Ok(Froyo {
            name: name.to_string(),
            devs: fds,
        })
    }
}

fn list(args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from list()");
    Ok(())
}

fn status(args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from status()");
    Ok(())
}

fn add(args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from add()");
    Ok(())
}

fn remove(args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from remove()");
    Ok(())
}

fn create(args: &ArgMatches) -> Result<(), FroyoError> {
    let name = args.value_of("froyodevname").unwrap();
    let dev_paths: Vec<_> = args.values_of("devices").unwrap().into_iter()
        .map(|dev| {
            if !Path::new(dev).is_absolute() {
                PathBuf::from(format!("/dev/{}", dev))
            } else {
                PathBuf::from(dev)
            }})
        .collect();

    let mut fds = Vec::new();
    for path in dev_paths {
        let dev = match Device::from_str(&path.to_string_lossy()) {
            Ok(x) => x,
            Err(_) => return Err(FroyoError::Io(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a block device", path.display()))))
        };

        dbgp!("Initializing {}", &path.display());
        let fd = try!(FroyoDev::initialize(dev, args.is_present("force")));
        fds.push(fd);
    }

    // TODO: Build froyodev on top of our newly created blockdevs
    let froyo = try!(Froyo::create(name, fds));

    dbgp!("Created {}", name);

    Ok(())
}

fn main() {

    let matches = App::new("froyo")
        .version(&crate_version!())
        .author("Andy Grover <andy@groveronline.com>")
        .about("Drobo + Free + YOLO")
        .arg(Arg::with_name("debug")
             .short("d")
             .long("debug")
             .help("Print additional output for debugging")
             )
        .subcommand(SubCommand::with_name("list")
                    .about("List all froyodevs")
                    .arg(Arg::with_name("long")
                         .short("l")
                         .long("long")
                         .help("Use a long listing format")
                         )
                    )
        .subcommand(SubCommand::with_name("status")
                    .about("Get the status of a single froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .required(true)
                         .help("Froyodev to get info on")
                         .index(1)
                         )
                    )
        .subcommand(SubCommand::with_name("add")
                    .about("Add one or more additional block devices to a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to add the device to")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("device(s) to add")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("remove")
                    .about("Remove a block device from a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to remove the device from")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Block device(s) to remove")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("create")
                    .about("Create a new froyodev")
                    .arg(Arg::with_name("force")
                         .short("f")
                         .long("force")
                         .help("Force")
                         )
                    .arg(Arg::with_name("froyodevname")
                         .help("Name of the new froyodev")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Initial block device(s) to use")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .get_matches();

    if matches.is_present("debug") {
        // must use unsafe to change a mut static, sigh
        unsafe { debug = true };
    }

    let r = match matches.subcommand() {
        ("list", Some(matches)) => list(matches),
        ("status", Some(matches)) => status(matches),
        ("add", Some(matches)) => add(matches),
        ("remove", Some(matches)) => remove(matches),
        ("create", Some(matches)) => create(matches),
        ("", None) => {
            println!("No command given, try \"help\"");
            Ok(())
        }
        _ => unreachable!(),
    };

    if let Err(r) = r {
        errp!("{}", r.description());
        exit(1);
    };
}
<commit_msg>Moar development<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/.

#![feature(slice_bytes, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate devicemapper;
#[macro_use]
extern crate clap;
extern crate nix;
extern crate crc;
extern crate byteorder;
extern crate uuid;
extern crate time;
extern crate serde;
extern crate serde_json;

#[allow(unused_imports)]
use std::io;
use std::io::{Read, Write, ErrorKind, Seek, SeekFrom};
use std::error::Error;
use std::process::exit;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::slice::bytes::copy_memory;
use std::os::unix::prelude::AsRawFd;
use std::fmt;

use devicemapper::{DM, Device, DmFlags};
use clap::{App, Arg, SubCommand, ArgMatches};
use nix::sys::{stat, ioctl};
use crc::crc32;
use byteorder::{LittleEndian, ByteOrder};
use uuid::Uuid;

const SECTOR_SIZE: usize = 512;
const FRO_HEADER_SIZE: usize = 512;
const FRO_MDA_ZONE_SIZE: usize = (1024 * 1024);
const FRO_MAGIC: &'static [u8] = b"!IamFroy0\x86\xffGO\x02^\x41";

// No devs smaller than a gig
const MIN_DEV_SIZE: usize = (1024 * 1024 * 1024);

static mut debug: bool = false;

macro_rules! dbgp {
    ($($arg:tt)*) => (
        unsafe {
            if debug {
                println!($($arg)*)
            }
        })
}

macro_rules! errp {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        })
}

// Define a common error enum.
// See http://blog.burntsushi.net/rust-error-handling/
#[derive(Debug)]
enum FroyoError {
    Io(io::Error),
    Serde(serde_json::error::Error),
}

impl fmt::Display for FroyoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FroyoError::Io(ref err) => write!(f, "IO error: {}", err),
            FroyoError::Serde(ref err) => write!(f, "Serde error: {}", err),
        }
    }
}

impl Error for FroyoError {
    fn description(&self) -> &str {
        match *self {
            FroyoError::Io(ref err) => err.description(),
            FroyoError::Serde(ref err) => Error::description(err),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            FroyoError::Io(ref err) => Some(err),
            FroyoError::Serde(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for FroyoError {
    fn from(err: io::Error) -> FroyoError {
        FroyoError::Io(err)
    }
}

impl From<serde_json::error::Error> for FroyoError {
    fn from(err: serde_json::error::Error) -> FroyoError {
        FroyoError::Serde(err)
    }
}

fn blkdev_size(file: &File) -> io::Result<u64> {
    // BLKGETSIZE64
    let op = ioctl::op_read(0x12, 114, 8);
    let mut val: u64 = 0;

    match unsafe { ioctl::read_into(file.as_raw_fd(), op, &mut val) } {
        Err(_) => return Err((io::Error::last_os_error())),
        Ok(_) => Ok(val),
    }
}

#[derive(Debug, Clone, Serialize)]
struct FroyoDev {
    dev: Device,
    path: PathBuf,
}

impl FroyoDev {
    fn new(path: &Path) -> io::Result<FroyoDev> {
        let dev = match Device::from_str(&path.to_string_lossy()) {
            Ok(x) => x,
            Err(_) => return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a block device", path.display())))
        };

        match stat::stat(path) {
            Err(_) => return Err(io::Error::new(
                ErrorKind::NotFound,
                format!("{} not found", path.display()))),
            Ok(x) => x,
        };

        let mut f = match OpenOptions::new().read(true).open(path) {
            Err(_) => {
                return Err(io::Error::new(
                    ErrorKind::PermissionDenied,
                    format!("Could not open {}", path.display())));
            },
            Ok(x) => x,
        };

        let mut buf = [0u8; FRO_HEADER_SIZE];
        try!(f.read(&mut buf));

        if &buf[4..20] != FRO_MAGIC {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a Froyo device", path.display())));
        }

        let crc = crc32::checksum_ieee(&buf[4..FRO_HEADER_SIZE]);
        if crc != LittleEndian::read_u32(&mut buf[..4]) {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} Froyo header CRC failed", path.display())));
        }

        Ok(FroyoDev {dev: dev, path: path.to_owned()})
    }

    fn initialize(path: &Path, force: bool) -> io::Result<FroyoDev> {
        let pstat = match stat::stat(path) {
            Err(_) => return Err(io::Error::new(
                ErrorKind::NotFound,
                format!("{} not found", path.display()))),
            Ok(x) => x,
        };

        if pstat.st_mode & 0x6000 != 0x6000 {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a block device", path.display())));
        }

        let mut f = match OpenOptions::new().read(true).write(true).open(path) {
            Err(_) => {
                return Err(io::Error::new(
                    ErrorKind::PermissionDenied,
                    format!("Could not open {}", path.display())));
            },
            Ok(x) => x,
        };

        if !force {
            let mut buf = [0u8; 4096];
            try!(f.read(&mut buf));

            if buf.iter().any(|x| *x != 0) {
                return Err(io::Error::new(
                    ErrorKind::InvalidInput,
                    format!("First 4K of {} is not zeroed, need to use --force",
                            path.display())));
            }
        }

        let dev_size = try!(blkdev_size(&f));
        if dev_size < MIN_DEV_SIZE as u64 {
            return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} too small, 1G minimum", path.display())));
        }

        let mut buf = [0u8; FRO_MDA_ZONE_SIZE];

        copy_memory(FRO_MAGIC, &mut buf[4..20]);
        LittleEndian::write_u64(&mut buf[20..28], dev_size / SECTOR_SIZE as u64);
        // no flags
        copy_memory(Uuid::new_v4().to_simple_string().as_bytes(), &mut buf[32..64]);
        // no MDAs in use yet

        // All done, calc CRC and write
        let crc = crc32::checksum_ieee(&buf[4..FRO_HEADER_SIZE]);
        LittleEndian::write_u32(&mut buf[..4], crc);

        try!(f.seek(SeekFrom::Start(0)));
        try!(f.write_all(&buf));
        try!(f.seek(SeekFrom::End(-(FRO_MDA_ZONE_SIZE as i64))));
        try!(f.write_all(&buf));

        try!(f.flush());

        let dev = match Device::from_str(&path.to_string_lossy()) {
            Ok(x) => x,
            Err(_) => return Err(io::Error::new(
                ErrorKind::InvalidInput,
                format!("{} is not a block device", path.display())))
        };

        Ok(FroyoDev {dev: dev, path: path.to_owned()})
    }
}

#[derive(Debug, Serialize)]
struct Froyo {
    name: String,
    devs: Vec<FroyoDev>,
}

impl Froyo {
    fn create(name: &str, fds: Vec<FroyoDev>) -> Result<Froyo, FroyoError> {

        let dm = try!(DM::new());

        try!(dm.device_create(&format!("froyo-{}", 1), None, DmFlags::empty()));

        Ok(Froyo {
            name: name.to_string(),
            devs: fds,
        })
    }
}

fn list(_args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from list()");
    Ok(())
}

fn status(_args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from status()");
    Ok(())
}

fn add(_args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from add()");
    Ok(())
}

fn remove(_args: &ArgMatches) -> Result<(), FroyoError> {
    println!("hello from remove()");
    Ok(())
}

fn create(args: &ArgMatches) -> Result<(), FroyoError> {
    let name = args.value_of("froyodevname").unwrap();
    let dev_paths: Vec<_> = args.values_of("devices").unwrap().into_iter()
        .map(|dev| {
            if !Path::new(dev).is_absolute() {
                PathBuf::from(format!("/dev/{}", dev))
            } else {
                PathBuf::from(dev)
            }})
        .collect();

    if dev_paths.len() < 2 {
        return Err(FroyoError::Io(io::Error::new(
            ErrorKind::InvalidInput, "At least 2 block devices must be given")))
    }

    if dev_paths.len() > 8 {
        return Err(FroyoError::Io(io::Error::new(
            ErrorKind::InvalidInput,
            format!("Max supported devices is 8, {} given", dev_paths.len()))))
    }

    let mut fds = Vec::new();
    for pathbuf in dev_paths {
        dbgp!("Initializing {}", &pathbuf.display());
        let fd = try!(FroyoDev::initialize(&pathbuf, args.is_present("force")));
        fds.push(fd);
    }

    // TODO: Build froyodev on top of our newly created blockdevs
    let froyo = try!(Froyo::create(name, fds));

    dbgp!("Created {}", name);
    println!("sss {}", try!(serde_json::to_string(&froyo)));

    Ok(())
}

fn main() {

    let matches = App::new("froyo")
        .version(&crate_version!())
        .author("Andy Grover <andy@groveronline.com>")
        .about("Drobo + Free + YOLO")
        .arg(Arg::with_name("debug")
             .short("d")
             .long("debug")
             .help("Print additional output for debugging")
             )
        .subcommand(SubCommand::with_name("list")
                    .about("List all froyodevs")
                    .arg(Arg::with_name("long")
                         .short("l")
                         .long("long")
                         .help("Use a long listing format")
                         )
                    )
        .subcommand(SubCommand::with_name("status")
                    .about("Get the status of a single froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .required(true)
                         .help("Froyodev to get info on")
                         .index(1)
                         )
                    )
        .subcommand(SubCommand::with_name("add")
                    .about("Add one or more additional block devices to a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to add the device to")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("device(s) to add")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("remove")
                    .about("Remove a block device from a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to remove the device from")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Block device(s) to remove")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("create")
                    .about("Create a new froyodev")
                    .arg(Arg::with_name("force")
                         .short("f")
                         .long("force")
                         .help("Force")
                         )
                    .arg(Arg::with_name("froyodevname")
                         .help("Name of the new froyodev")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Initial block device(s) to use")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .get_matches();

    if matches.is_present("debug") {
        // must use unsafe to change a mut static, sigh
        unsafe { debug = true };
    }

    let r = match matches.subcommand() {
        ("list", Some(matches)) => list(matches),
        ("status", Some(matches)) => status(matches),
        ("add", Some(matches)) => add(matches),
        ("remove", Some(matches)) => remove(matches),
        ("create", Some(matches)) => create(matches),
        ("", None) => {
            println!("No command given, try \"help\"");
            Ok(())
        }
        _ => unreachable!(),
    };

    if let Err(r) = r {
        errp!("{}", r.description());
        exit(1);
    };
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate futures;
extern crate tokio_core;

use std::env;
use std::io::{Read, Write};
use std::net::SocketAddr;

use futures::Future;
use futures::stream::Stream;
use tokio_core::Loop;

fn main() {
    env_logger::init().unwrap();

    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let backend = env::args().nth(2).unwrap_or("127.0.0.1:12345".to_string());
    let backend = backend.parse::<SocketAddr>().unwrap();

    // Create the event loop that will drive this server
    let mut lp = Loop::new().unwrap();
    let handle = lp.handle();

    // Create a TCP listener which will listen for incoming connections
    let listener = lp.run(handle.clone().tcp_listen(&addr)).unwrap();

    let pin = lp.pin();

    println!("Listening on: {}", addr);

    let clients = listener.incoming().map(move |(socket, addr)| {
        println!("Incoming connection on {}", addr);
        socket
    });

    let pairs = clients.map(|client| {
        let handle = handle.clone();
        let connected = handle.tcp_connect(&backend);

        connected.and_then(move |server| Ok((client, server)))
    });

    let server = pairs.for_each(|pair| {
        println!("Resolving pairs");
        pin.spawn(pair.and_then(|(mut client, _server)| {
            println!("About to read");

            let mut buf = Vec::new();
            let bytes = client.read_to_end(&mut buf).expect("Failed to read from client");

            println!("Read {} bytes", bytes);
            futures::finished(())
        }).map_err(|e| { // spawn expects an error type of () and we are passing through io::Error
            println!("Error when reading from client - {}", e);
            ()
        }));

        Ok(())
    });

    lp.run(server).unwrap();
}
<commit_msg>Create a Pipe object instead of raw streams<commit_after>extern crate env_logger;
extern crate futures;
extern crate tokio_core;

use std::env;
use std::io::{Read, Write};
use std::net::SocketAddr;

use futures::Future;
use futures::stream::Stream;
use tokio_core::{Loop, TcpStream};

struct Pipe {
    client: TcpStream,
    server: TcpStream,
    buf: Vec<u8>,
}

fn main() {
    env_logger::init().unwrap();

    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let backend = env::args().nth(2).unwrap_or("127.0.0.1:12345".to_string());
    let backend = backend.parse::<SocketAddr>().unwrap();

    // Create the event loop that will drive this server
    let mut lp = Loop::new().unwrap();
    let handle = lp.handle();

    // Create a TCP listener which will listen for incoming connections
    let listener = lp.run(handle.clone().tcp_listen(&addr)).unwrap();

    let pin = lp.pin();

    println!("Listening on: {}", addr);

    let pipe = listener.incoming().map(move |(socket, addr)| {
        println!("Incoming connection on {}", addr);

        let handle = handle.clone();
        let connected = handle.tcp_connect(&backend);

        connected.and_then(move |server| {
            Ok (
                Pipe {
                    client: socket,
                    server: server,
                    buf: Vec::new(),
                }
            )
        })
    });

    //let pipe = pipe.and_then(|mut pipe| {
    //    let bytes = pipe.client.read_to_end(&mut pipe.buf).expect("Failed to read from client");

    //    println!("Read {} bytes", bytes);
    //    pipe
    //});

    let server = pipe.for_each(|pipe| {
        println!("Connecting pipe");
        pin.spawn(pipe.and_then(|mut pipe| {
            println!("About to read");

            let bytes = pipe.client.read_to_end(&mut pipe.buf).expect("Failed to read from client");

            println!("Read {} bytes", bytes);

            futures::finished(())
        }).map_err(|e| { // spawn expects an error type of () and we are passing through io::Error
            println!("Error when reading from client - {}", e);
            ()
        }));

        Ok(())
    });

    lp.run(server).unwrap();
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_io::IoStream;
use std::mem;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::{discovery, DiscoveryStream};
use librespot::mixer::{self, Mixer};
use librespot::player::Player;
use librespot::spirc::{Spirc, SpircTask};

fn usage(program: &str, opts: &getopts::Options) -> String {
    let brief = format!("Usage: {} [options]", program);
    opts.usage(&brief)
}

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
        .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
        .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
        .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!("librespot {} ({}). Built on {}. Build ID: {}",
             version::short_sha(),
             version::commit_date(),
             version::short_now(),
             version::build_id());

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name)
        .expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref())
        .expect("Invalid mixer");
    let initial_volume;
    // check if initial-volume argument is present
    if matches.opt_present("initial-volume"){
        // check if value is a number
        if matches.opt_str("initial-volume").unwrap().parse::<i32>().is_ok(){
            // check if value is in [0-100] range, otherwise put the bound values
            if matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap() < 0 {
                initial_volume = 0 as i32;
            }
            else if matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap() > 100{
                initial_volume = 0xFFFF as i32;
            }
            // checks ok
            else{
                initial_volume = matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap()* 0xFFFF as i32 / 100 ;
            }
        }
        // if value is not a number use default value (50%)
        else {
            initial_volume = 0x8000 as i32;
        }
    }
    // if argument not present use default values (50%)
    else{
        initial_volume = 0x8000 as i32;
        }
    debug!("Volume \"{}\" !", initial_volume);

    let name = matches.opt_str("name").unwrap();
    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches.opt_str("c").map(|cache_location| {
        Cache::new(PathBuf::from(cache_location), use_audio_cache)
    });

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials
        )
    };

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    let player_config = {
        let bitrate = matches.opt_str("b").as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    let connect_config = {
        let device_type = matches.opt_str("device-type").as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        mixer: mixer,
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item=Session, Error=io::Error>>,

    shutdown: bool,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let player = Player::new(player_config, session.clone(), audio_filter, move || {
                    (backend)(device)
                });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let args: Vec<String> = std::env::args().collect();

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<commit_msg>removed and optimised<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_io::IoStream;
use std::mem;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::{discovery, DiscoveryStream};
use librespot::mixer::{self, Mixer};
use librespot::player::Player;
use librespot::spirc::{Spirc, SpircTask};

fn usage(program: &str, opts: &getopts::Options) -> String {
    let brief = format!("Usage: {} [options]", program);
    opts.usage(&brief)
}

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
        .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
        .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
        .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!("librespot {} ({}). Built on {}. Build ID: {}",
             version::short_sha(),
             version::commit_date(),
             version::short_now(),
             version::build_id());

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name)
        .expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref())
        .expect("Invalid mixer");

    let initial_volume;
        if matches.opt_present("initial-volume") && matches.opt_str("initial-volume").unwrap().parse::<i32>().is_ok() {
            let iv = matches.opt_str("zeroconf-port").unwrap().parse::<u16>().unwrap();
            if iv => 0 && iv <= 100 {
                initial_volume = iv * 0xFFFF as i32 / 100 ;
            } else {
                debug!("Volume needs to be a value from 0-100; set as 50%");
                initial_volume = 0x8000 as i32;
            }
        } else {
            initial_volume = 0x8000 as i32;
        }

    let name = matches.opt_str("name").unwrap();
    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches.opt_str("c").map(|cache_location| {
        Cache::new(PathBuf::from(cache_location), use_audio_cache)
    });

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials
        )
    };

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    let player_config = {
        let bitrate = matches.opt_str("b").as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    let connect_config = {
        let device_type = matches.opt_str("device-type").as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        mixer: mixer,
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item=Session, Error=io::Error>>,

    shutdown: bool,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let player = Player::new(player_config, session.clone(), audio_filter, move || {
                    (backend)(device)
                });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let args: Vec<String> = std::env::args().collect();

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<|endoftext|>