<commit_before>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_util;
extern crate bip_utracker;
extern crate chrono;
extern crate futures;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate tokio_core;
extern crate url;

mod btclient;
mod packet;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain!{}
}

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use btclient::BTClient;
use chrono::{TimeZone, UTC};
use errors::*;
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::{MutablePeerHandshakePacket, MutablePeerMessagePacket};
use pnet::packet::Packet;
use rustyline::completion::FilenameCompleter;
use rustyline::{Config, CompletionType, Editor};
use rustyline::error::ReadlineError;
use tokio_core::io::{read, write_all};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use url::Url;
use btclient::{Torrent, FileT};

use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::str;
use std::string::String;
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;
const PEER_REQ_PKT_SZ: usize = 14;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
    Ok(())
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes)?;
    Ok(())
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: String,
                      port: u16)
                      -> Result<(Vec<String>, String)> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", &peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    debug!("Peer list received:");
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn send_keep_alive(mut stream: &TcpStream) -> Result<()> {
    let buf = vec![0; 1];
    match stream.write(&buf) {
        Ok(_) => Ok(()),
        Err(e) => bail!("Writing to stream  failed! {:?}", e),
    }
}

fn peer_connections(peer_ip_ports: Vec<String>, info_hash: &str, peer_id: String) -> Result<()> {
    for ip_port in peer_ip_ports {
        let peer_ip_port = ip_port.clone();
        let info_hash_clone = info_hash.to_string().clone();
        let peer_id_clone = peer_id.clone();
        thread::spawn(move || {
            fn run(pip: String, ih: String, pid: String) -> Result<()> {
                let mut l = Core::new().unwrap();
                // TODO setup timeout before handshake
                // let dur = Duration::from_secs(100);
                // let timeout = Timeout::new(dur, &l.handle()).unwrap();

                if let Ok(client) = handshake_peer(&mut l, pip, ih, pid)
                    .chain_err(|| "handshake failed") {
                    // debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    // debug!("Starting thread timer...");
                    if let Ok((_, buf, amt)) =
                        l.run(read(client, vec![0; PEER_HANDSHAKE_STRUCT_SZ]))
                            .chain_err(|| "didn't receive handshake response from peer") {
                        if buf[0] == 19 &&
                           String::from_utf8_lossy(&buf[1..20]) == "BitTorrent protocol" &&
                           amt == 68 {
                            info!("handshake successful!");
                        } else {
                            info!("not a useful peer")
                        }
                    } else {
                        bail!("");
                    }
                } else {
                    bail!("");
                }
                Ok(())
            }
            if let Err(ref e) = run(peer_ip_port, info_hash_clone, peer_id_clone) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }

            // TODO determine the below three parameters logically
            // let index = 0;
            // let begin = 0;
            // let length = 10;
            // match request_piece(&stream, index, begin, length) {
            //     Ok(buf_read) => {
            //         trace!("buf_read: {:?}", buf_read);
            //     }
            //     Err(_) => error!("Requesting piece failed!"),
            // }
            // send_keep_alive(&stream);
        });
    }
    Ok(())
}

fn request_piece(mut stream: &TcpStream, index: u32, begin: u32, length: u32) -> Result<Vec<u8>> {
    let mut buf = vec![0u8; PEER_REQ_PKT_SZ];
    let mut peer_msg_pkt = MutablePeerMessagePacket::new(&mut buf).unwrap();
    peer_msg_pkt.set_len(13);
    peer_msg_pkt.set_id(6);
    let mut index_begin_length: Vec<u32> = Vec::new();
    index_begin_length.push(index);
    index_begin_length.push(begin);
    index_begin_length.push(length);
    let byte_arr = unsafe { std::mem::transmute::<Vec<u32>, Vec<u8>>(index_begin_length) };

    peer_msg_pkt.set_payload(&byte_arr);

    match stream.write(peer_msg_pkt.packet()) {
        Ok(_) => {
            let mut buf_read = vec![0; 2048];
            match stream.read(&mut buf_read) {
                Ok(bytes_read) => {
                    debug!("Bytes read: {:?}", bytes_read);
                    Ok(buf_read[..bytes_read].to_vec())
                }
                Err(e) => {
                    // error!("Read failed! {:?}", e);
                    bail!("Read failed! {:?}", e)
                }
            }
        }
        Err(e) => {
            bail!("Write to stream failed! {:?}", e)
            // Err("Write to stream failed!".to_owned())
        }
    }
}

fn handshake_peer(core: &mut Core,
                  peer_ip_port: String,
                  info_hash: String,
                  peer_id: String)
                  -> Result<TcpStream> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    let handle = core.handle();
    let client = TcpStream::connect(&peer_ip_port.parse::<SocketAddr>().unwrap(), &handle);
    let client = core.run(client).chain_err(|| "unable to connect to peer")?;
    let (client, _) = core.run(write_all(client, ph.packet())).unwrap();
    trace!("{:?}", client);
    Ok(client)
}

fn main() {
    pretty_env_logger::init();

    // See: https://brson.github.io/2016/11/30/starting-with-error-chain for explanation
    if let Err(ref e) = run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

fn run() -> Result<()> {
    let mut btclient = BTClient::new();

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .build();
    let c = FilenameCompleter::new();
    let mut rl = Editor::with_config(config);
    rl.set_completer(Some(c));
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_ref());

                let line = line.trim().split(' ').collect::<Vec<&str>>();

                match line[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
add/a <torrent file path>        - add a new torrent file for tracking
remove/r <torrent id>            - remove given torrent from tracking list
list/l                           - list torrents being tracked
showfiles/sf <torrent file path> - show files in the torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if line.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = line[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "add" | "a" => {
                        if line.len() != 2 {
                            error!("usage: add <torrent file>");
                        } else if let Ok(file) = File::open(line[1]) {
                            btclient.add(file)?;
                        } else {
                            error!("Unable to open file, are you sure it exists?");
                        }
                    }
                    "remove" | "r" => {
                        if line.len() != 2 {
                            error!("usage: remove <torrent number>");
                        } else {
                            let id = line[1].parse::<usize>().unwrap();
                            btclient.remove(id)?;
                        }
                    }
                    "list" | "l" => {
                        if line.len() != 1 {
                            error!("usage: list");
                        } else {
                            let t_list = btclient.list();
                            println!("ID  Torrent");
                            println!("--  ----------------------------------------------");
                            for t in t_list {
                                println!("{:2}  {}", t.0, t.1);
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if line.len() != 2 {
                            error!("usage: showfiles <torrent file>");
                        } else {
                            let path = line[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_files(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {
                info!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                info!("CTRL-D");
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
    Ok(())
}
<commit_msg>Rename run -> cli_run<commit_after>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_util;
extern crate bip_utracker;
extern crate chrono;
extern crate futures;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate tokio_core;
extern crate url;

mod btclient;
mod packet;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain!{}
}

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use btclient::BTClient;
use chrono::{TimeZone, UTC};
use errors::*;
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::{MutablePeerHandshakePacket, MutablePeerMessagePacket};
use pnet::packet::Packet;
use rustyline::completion::FilenameCompleter;
use rustyline::{Config, CompletionType, Editor};
use rustyline::error::ReadlineError;
use tokio_core::io::{read, write_all};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use url::Url;
use btclient::{Torrent, FileT};

use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::str;
use std::string::String;
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;
const PEER_REQ_PKT_SZ: usize = 14;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
    Ok(())
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes)?;
    Ok(())
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: String,
                      port: u16)
                      -> Result<(Vec<String>, String)> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", &peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    debug!("Peer list received:");
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn send_keep_alive(mut stream: &TcpStream) -> Result<()> {
    let buf = vec![0; 1];
    match stream.write(&buf) {
        Ok(_) => Ok(()),
        Err(e) => bail!("Writing to stream  failed! {:?}", e),
    }
}

fn peer_connections(peer_ip_ports: Vec<String>, info_hash: &str, peer_id: String) -> Result<()> {
    for ip_port in peer_ip_ports {
        let peer_ip_port = ip_port.clone();
        let info_hash_clone = info_hash.to_string().clone();
        let peer_id_clone = peer_id.clone();
        thread::spawn(move || {
            fn run(pip: String, ih: String, pid: String) -> Result<()> {
                let mut l = Core::new().unwrap();
                // TODO setup timeout before handshake
                // let dur = Duration::from_secs(100);
                // let timeout = Timeout::new(dur, &l.handle()).unwrap();

                if let Ok(client) = handshake_peer(&mut l, pip, ih, pid)
                    .chain_err(|| "handshake failed") {
                    // debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    // debug!("Starting thread timer...");
                    if let Ok((_, buf, amt)) =
                        l.run(read(client, vec![0; PEER_HANDSHAKE_STRUCT_SZ]))
                            .chain_err(|| "didn't receive handshake response from peer") {
                        if buf[0] == 19 &&
                           String::from_utf8_lossy(&buf[1..20]) == "BitTorrent protocol" &&
                           amt == 68 {
                            info!("handshake successful!");
                        } else {
                            info!("not a useful peer")
                        }
                    } else {
                        bail!("");
                    }
                } else {
                    bail!("");
                }
                Ok(())
            }
            if let Err(ref e) = run(peer_ip_port, info_hash_clone, peer_id_clone) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }

            // TODO determine the below three parameters logically
            // let index = 0;
            // let begin = 0;
            // let length = 10;
            // match request_piece(&stream, index, begin, length) {
            //     Ok(buf_read) => {
            //         trace!("buf_read: {:?}", buf_read);
            //     }
            //     Err(_) => error!("Requesting piece failed!"),
            // }
            // send_keep_alive(&stream);
        });
    }
    Ok(())
}

fn request_piece(mut stream: &TcpStream, index: u32, begin: u32, length: u32) -> Result<Vec<u8>> {
    let mut buf = vec![0u8; PEER_REQ_PKT_SZ];
    let mut peer_msg_pkt = MutablePeerMessagePacket::new(&mut buf).unwrap();
    peer_msg_pkt.set_len(13);
    peer_msg_pkt.set_id(6);
    let mut index_begin_length: Vec<u32> = Vec::new();
    index_begin_length.push(index);
    index_begin_length.push(begin);
    index_begin_length.push(length);
    let byte_arr = unsafe { std::mem::transmute::<Vec<u32>, Vec<u8>>(index_begin_length) };

    peer_msg_pkt.set_payload(&byte_arr);

    match stream.write(peer_msg_pkt.packet()) {
        Ok(_) => {
            let mut buf_read = vec![0; 2048];
            match stream.read(&mut buf_read) {
                Ok(bytes_read) => {
                    debug!("Bytes read: {:?}", bytes_read);
                    Ok(buf_read[..bytes_read].to_vec())
                }
                Err(e) => {
                    // error!("Read failed! {:?}", e);
                    bail!("Read failed! {:?}", e)
                }
            }
        }
        Err(e) => {
            bail!("Write to stream failed! {:?}", e)
            // Err("Write to stream failed!".to_owned())
        }
    }
}

fn handshake_peer(core: &mut Core,
                  peer_ip_port: String,
                  info_hash: String,
                  peer_id: String)
                  -> Result<TcpStream> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    let handle = core.handle();
    let client = TcpStream::connect(&peer_ip_port.parse::<SocketAddr>().unwrap(), &handle);
    let client = core.run(client).chain_err(|| "unable to connect to peer")?;
    let (client, _) = core.run(write_all(client, ph.packet())).unwrap();
    trace!("{:?}", client);
    Ok(client)
}

fn main() {
    pretty_env_logger::init();

    // See: https://brson.github.io/2016/11/30/starting-with-error-chain for explanation
    if let Err(ref e) = cli_run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

#[allow(cyclomatic_complexity)]
fn cli_run() -> Result<()> {
    let mut btclient = BTClient::new();

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .build();
    let c = FilenameCompleter::new();
    let mut rl = Editor::with_config(config);
    rl.set_completer(Some(c));
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_ref());

                let line = line.trim().split(' ').collect::<Vec<&str>>();

                match line[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
add/a <torrent file path>        - add a new torrent file for tracking
remove/r <torrent id>            - remove given torrent from tracking list
list/l                           - list torrents being tracked
showfiles/sf <torrent file path> - show files in the torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if line.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = line[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "add" | "a" => {
                        if line.len() != 2 {
                            error!("usage: add <torrent file>");
                        } else if let Ok(file) = File::open(line[1]) {
                            btclient.add(file)?;
                        } else {
                            error!("Unable to open file, are you sure it exists?");
                        }
                    }
                    "remove" | "r" => {
                        if line.len() != 2 {
                            error!("usage: remove <torrent number>");
                        } else {
                            let id = line[1].parse::<usize>().unwrap();
                            btclient.remove(id)?;
                        }
                    }
                    "list" | "l" => {
                        if line.len() != 1 {
                            error!("usage: list");
                        } else {
                            let t_list = btclient.list();
                            println!("ID  Torrent");
                            println!("--  ----------------------------------------------");
                            for t in t_list {
                                println!("{:2}  {}", t.0, t.1);
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if line.len() != 2 {
                            error!("usage: showfiles <torrent file>");
                        } else {
                            let path = line[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_files(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {
                info!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                info!("CTRL-D");
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
    Ok(())
}
<|endoftext|>


<commit_before>extern crate ww;

use ww::map::{Map, MapRoomId};
use ww::room::Room;
use ww::platform;
use ww::direction::Direction::*;
use ww::command::{PrimaryCommand, CommandProcessor};

#[cfg(target_os = "emscripten")]
use ww::platform::emscripten;

use RoomId::*;

const MAX_ROOMS: usize = 20;

#[derive(Debug, Copy, Clone)]
enum RoomId {
  Hallway = 1,
  AudienceChamber = 2,
}

fn build_world(map: &mut Map<RoomId>) {
  map.room(Hallway).describe(
    "Hallway",
    "You are in the hallway. \
     There is a door to the south. \
     Through the windows to the north you can see a secret herb garden."
  );

  map.room(AudienceChamber).describe(
    "Audience Chamber",
    "This is the audience chamber. \
     There is a window to the west. By looking to the right \
     through it you can see the entrance to the castle. \
     Doors leave this room to the north, east, and south."
  );

  map.connect(Hallway, South, AudienceChamber);
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}

#[derive(Debug, PartialEq, Copy, Clone)]
enum GameMode {
  AskName,
  Primary,
  Finished,
}

struct GameState<'a> {
  map: &'a mut Map<'a, RoomId>,
  pub curr_mode: GameMode,
  player_name: String,
  curr_room: RoomId,
  show_desc: bool,
}

impl<'a> GameState<'a> {
  pub fn new(map: &'a mut Map<'a, RoomId>) -> Self {
    Self {
      map: map,
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: Hallway,
      show_desc: true,
    }
  }

  pub fn tick_primary_mode(&mut self) {
    if self.show_desc {
      println!("{}", self.map.room(self.curr_room).description);
      self.show_desc = false;
    }

    if let Some(cmd) = PrimaryCommand::get() {
      match cmd {
        PrimaryCommand::Go(dir) => {
          if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
            self.curr_room = room;
            self.show_desc = true;
          } else {
            println!("You can't go that way.");
          }
        },
        PrimaryCommand::Look => { self.show_desc = true; }
        PrimaryCommand::Quit => { self.curr_mode = GameMode::Finished; }
      }
    };
  }

  pub fn tick(&mut self) {
    match self.curr_mode {
      GameMode::AskName => {
        platform::show_prompt("What is your name, explorer? ");

        match platform::read_input() {
          Some(input) => {
            if input.len() == 0 {
              println!("Pardon me?");
            } else {
              self.player_name = input;
              self.curr_mode = GameMode::Primary;
            }
          },
          None => {}
        }
      },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Finished => {}
    }
  }
}

fn main() {
  let mut rooms = [Room::new(); MAX_ROOMS];
  let mut map = Map::new(&mut rooms);

  build_world(&mut map);

  let mut state = GameState::new(&mut map);

  println!("Werewolves and Wanderer\n");

  ::ww::platform::sleep(500);

  #[cfg(target_os = "emscripten")]
  emscripten::set_main_loop_callback(|| state.tick());

  #[cfg(not(target_os = "emscripten"))]
  while state.curr_mode != GameMode::Finished {
    state.tick();
  }

  println!("Farewell.");
}
<commit_msg>Use Option::map() instead of matching.<commit_after>extern crate ww;

use ww::map::{Map, MapRoomId};
use ww::room::Room;
use ww::platform;
use ww::direction::Direction::*;
use ww::command::{PrimaryCommand, CommandProcessor};

#[cfg(target_os = "emscripten")]
use ww::platform::emscripten;

use RoomId::*;

const MAX_ROOMS: usize = 20;

#[derive(Debug, Copy, Clone)]
enum RoomId {
  Hallway = 1,
  AudienceChamber = 2,
}

fn build_world(map: &mut Map<RoomId>) {
  map.room(Hallway).describe(
    "Hallway",
    "You are in the hallway. \
     There is a door to the south. \
     Through the windows to the north you can see a secret herb garden."
  );

  map.room(AudienceChamber).describe(
    "Audience Chamber",
    "This is the audience chamber. \
     There is a window to the west. By looking to the right \
     through it you can see the entrance to the castle. \
     Doors leave this room to the north, east, and south."
  );

  map.connect(Hallway, South, AudienceChamber);
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}

#[derive(Debug, PartialEq, Copy, Clone)]
enum GameMode {
  AskName,
  Primary,
  Finished,
}

struct GameState<'a> {
  map: &'a mut Map<'a, RoomId>,
  pub curr_mode: GameMode,
  player_name: String,
  curr_room: RoomId,
  show_desc: bool,
}

impl<'a> GameState<'a> {
  pub fn new(map: &'a mut Map<'a, RoomId>) -> Self {
    Self {
      map: map,
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: Hallway,
      show_desc: true,
    }
  }

  pub fn tick_primary_mode(&mut self) {
    if self.show_desc {
      println!("{}", self.map.room(self.curr_room).description);
      self.show_desc = false;
    }

    if let Some(cmd) = PrimaryCommand::get() {
      match cmd {
        PrimaryCommand::Go(dir) => {
          if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
            self.curr_room = room;
            self.show_desc = true;
          } else {
            println!("You can't go that way.");
          }
        },
        PrimaryCommand::Look => { self.show_desc = true; }
        PrimaryCommand::Quit => { self.curr_mode = GameMode::Finished; }
      }
    };
  }

  pub fn tick(&mut self) {
    match self.curr_mode {
      GameMode::AskName => {
        platform::show_prompt("What is your name, explorer? ");

        platform::read_input().map(|input| {
          if input.len() == 0 {
            println!("Pardon me?");
          } else {
            self.player_name = input;
            self.curr_mode = GameMode::Primary;
          }
        });
      },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Finished => {}
    }
  }
}

fn main() {
  let mut rooms = [Room::new(); MAX_ROOMS];
  let mut map = Map::new(&mut rooms);

  build_world(&mut map);

  let mut state = GameState::new(&mut map);

  println!("Werewolves and Wanderer\n");

  ::ww::platform::sleep(500);

  #[cfg(target_os = "emscripten")]
  emscripten::set_main_loop_callback(|| state.tick());

  #[cfg(not(target_os = "emscripten"))]
  while state.curr_mode != GameMode::Finished {
    state.tick();
  }

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


<commit_before>extern crate hyper;
extern crate chrono;

use std::env;
use std::io::Read;
use chrono::*;
use hyper::Client;
use hyper::header::Connection;

static WEEKDAY_WEBSITE: &'static str = "http://www.hvilkenukeerdet.no";

fn print_help() {
    println!("`uke` gives the current week number.
             `uke <number>` gives you the range for that week number");
}

fn parse_string_to_int(s: String) -> usize {
    let mut res: usize = 0;
    let bytes = s.into_bytes();
    let b_len = bytes.len();
    for i in 0..b_len {
        // 48 is the byte number for the string "0"
        let natural_number = (bytes[(b_len-1) - i] as usize) - 48;
        if i > 0 {
            res += i * 10 * natural_number
        } else {
            res += natural_number;
        }
    }

    if res > 52 {
        return 0;
    } else {
        return res;
    }
}

fn show_week_period_for_week_number(w: usize) {
    let year = Local::now().year();
    let mut start_date = UTC.ymd(year, 1, 1).and_hms(1,0,0);
    let mut current_week = 1;

    // week 1 is defined by the first week with a thursday
    // in a year

    let jan_1_week_day = start_date.weekday().number_from_monday();
    if jan_1_week_day > 4 {
        /* this week has no thursday */
        /* the calculation will give the date of monday in the first week*/
        start_date = UTC.ymd(year, 1, (1 + 8-jan_1_week_day)).and_hms(1,0,0);
    } else {
        /* start at this week's first day */
        start_date = UTC.ymd(year-1, 12, (31 - (jan_1_week_day - 2))).and_hms(1,0,0);
    }

    while w != current_week {
        start_date = start_date + Duration::weeks(1);
        current_week += 1;
    }

    let end_date = start_date + Duration::days(6);

    println!("Week {}, {} is from \"{}\" to \"{}\"",
             w,
             year,
             start_date.format("%d %b"),
             end_date.format("%d %b")
            );
}

fn main() {
    if let Some(arg1) = env::args().nth(1) {
        let week_number = parse_string_to_int(arg1);
        if week_number == 0 {
            print_help();
        } else {
            show_week_period_for_week_number(week_number);
        }
    } else {
        let client = Client::new();

        let mut res = client
                        .get(WEEKDAY_WEBSITE)
                        .header(Connection::close())
                        .send()
                        .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        let body_index = body.find("\n").unwrap() + "\n".len();
        let break_index = body.find("
").unwrap();

        let week_number = &body[body_index..break_index];
        println!("{}", week_number);
    }
}
<commit_msg>Simplify parsing of string from command line<commit_after>extern crate hyper;
extern crate chrono;

use std::env;
use std::io::Read;
use chrono::*;
use hyper::Client;
use hyper::header::Connection;

static WEEKDAY_WEBSITE: &'static str = "http://www.hvilkenukeerdet.no";

fn print_help() {
    println!("`uke` gives the current week number.
             `uke <number>` gives you the range for that week number");
}

fn show_week_period_for_week_number(w: usize) {
    let year = Local::now().year();
    let mut start_date = UTC.ymd(year, 1, 1).and_hms(1,0,0);
    let mut current_week = 1;

    // week 1 is defined by the first week with a thursday
    // in a year

    let jan_1_week_day = start_date.weekday().number_from_monday();
    if jan_1_week_day > 4 {
        /* this week has no thursday */
        /* the calculation will give the date of monday in the first week*/
        start_date = UTC.ymd(year, 1, (1 + 8-jan_1_week_day)).and_hms(1,0,0);
    } else {
        /* start at this week's first day */
        start_date = UTC.ymd(year-1, 12, (31 - (jan_1_week_day - 2))).and_hms(1,0,0);
    }

    while w != current_week {
        start_date = start_date + Duration::weeks(1);
        current_week += 1;
    }

    let end_date = start_date + Duration::days(6);

    println!("Week {}, {} is from \"{}\" to \"{}\"",
             w,
             year,
             start_date.format("%d %b"),
             end_date.format("%d %b")
            );
}

fn main() {
    if let Some(arg1) = env::args().nth(1) {
        match arg1.parse::<usize>() {
            Ok(week_number) =>
                if week_number >= 1 && week_number <= 52 {
                    show_week_period_for_week_number(week_number);
                    return;
                },
            _ => {} ,
        }

        print_help();
    } else {
        let client = Client::new();

        let mut res = client
                        .get(WEEKDAY_WEBSITE)
                        .header(Connection::close())
                        .send()
                        .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        let body_index = body.find("\n").unwrap() + "\n".len();
        let break_index = body.find("
").unwrap();

        let week_number = &body[body_index..break_index];
        println!("{}", week_number);
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_files(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut files = HashMap::new();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = entry.metadata().unwrap();

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        files.entry(metadata.len()).or_insert(Vec::new()).push(path);
    }

    files
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_files(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        // Avoid hardlinks to same inode
        let mut hardlinks = Vec::new();
        let mut di = HashSet::new();
        for f_path in f_paths {
            let metadata = f_path.metadata().unwrap();
            let dev_inode = (metadata.st_dev(), metadata.st_ino());
            if !di.contains(&dev_inode) {
                hardlinks.push(f_path);
                di.insert(dev_inode);
            }
        }

        let mut hashes = HashMap::new();
        for f_path in hardlinks {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<commit_msg>Clean up collect_paths function<commit_after>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_paths(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut length_paths = HashMap::new();

    for entry in WalkDir::new(path) {
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                println!("dupr: {}", err); // stderr
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = match entry.metadata() {
            Ok(e) => e,
            Err(err) => {
                println!("dupr: {}", err); //std err
                continue;
            }
        };

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        length_paths
            .entry(metadata.len())
            .or_insert(Vec::new())
            .push(path);
    }

    length_paths
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_paths(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        // Avoid hardlinks to same inode
        let mut hardlinks = Vec::new();
        let mut di = HashSet::new();
        for f_path in f_paths {
            let metadata = f_path.metadata().unwrap();
            let dev_inode = (metadata.st_dev(), metadata.st_ino());
            if !di.contains(&dev_inode) {
                hardlinks.push(f_path);
                di.insert(dev_inode);
            }
        }

        let mut hashes = HashMap::new();
        for f_path in hardlinks {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<|endoftext|>


<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

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

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

extern crate ncurses;
use ncurses::*;

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

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    let mut cursorstate:usize = 0; //0 is left nibble, 1 is right nibble, 2 is ascii
    const SPALTEN:usize = 16;
    let mut command = String::new();

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

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

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

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

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

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

    getch();
    clear();

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

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

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

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

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

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

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

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => { // Button "h"
                    if cursorstate == 1 { cursorstate = 0;}
                    else if cursorpos != 0 { cursorstate = 1; cursorpos-=1}
                },
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => { // Button "l"
                    if cursorstate == 0 { cursorstate = 1; }
                    else if cursorstate == 1 && cursorpos != buffer.len()-1 { cursorstate = 0; cursorpos+=1; }
                },
                 48 => { // Start of line is "to start"
                     cursorpos -= cursorpos%16;
                     cursorstate = 0;
                },
                 36 => { //dollar is "to end"
                     if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)}
                        else {cursorpos = buffer.len()-1};
                    cursorstate = 1;
                },
                114 => mode = 1, //r replaces the next char
                120 => {buffer.remove(cursorpos);}, //x remove the next char
                105 => mode = 3, //i goes to insert mode
                 58 => {mode = 2; command.clear();}, // ":"
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 && cursorstate == 2 { // r was pressed so replace next char in ascii mode
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8 },
                _ => (),
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 0 {
            match key { // Change left nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 55)<<4 },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 48)<<4 },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 1 {
            match key { // Change right nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55) },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48) },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); },
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        } else
        if mode == 3 {
            if cursorstate == 0 { // Left nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        {buffer.insert(cursorpos, (c as u8 - 55)<<4 ); cursorstate = 1;},
                    c @ 48... 57 => // 0-9
                        {buffer.insert(cursorpos, (c as u8 - 48)<<4); cursorstate = 1;},
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 1 { // Right nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55); cursorstate = 0; cursorpos+=1; },
                    c @ 48...57 => // 0-9
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48); cursorstate = 0; cursorpos+=1; },
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 2 { // Ascii
                match key {
                    c @ 32...126 => { buffer.insert(cursorpos, c as u8); },
                    27 => {mode = 0;},
                    _ => (),
                }
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);
    }

    refresh();
    endwin();
}

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

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

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s < buffer.len() {
                if z*spalten+s == cursorpos && cursorstate == 0 { attron(COLOR_PAIR(1)); } // Color of left nibble
                printw(&format!("{:01X}", buffer[z*spalten+s]>>4) ); // Display left nibble
                if z*spalten+s == cursorpos && cursorstate == 0 { attroff(COLOR_PAIR(1)); }

                if z*spalten+s == cursorpos && cursorstate == 1 { attron(COLOR_PAIR(1)); } // Color of right nibble
                printw(&format!("{:01X} ", buffer[z*spalten+s]&0x0F) ); // Display right nibble
                if z*spalten+s == cursorpos && cursorstate == 1 { attroff(COLOR_PAIR(1)); }
            } else {
                printw("-- ");
            }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos && cursorstate == 2 { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos && cursorstate == 2 { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
    for _ in 0 .. maxzeilen-zeilen-2 {
        printw("\n"); // Put the cursor on last line of terminal
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
    }
    if mode == 3 {
        printw("insert"); // Indicate that insert mode is active
    }
    printw(&format!("{}", command));
}
<commit_msg>Implement ascii mode<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

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

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

extern crate ncurses;
use ncurses::*;

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

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    let mut cursorstate:usize = 0; //0 is left nibble, 1 is right nibble, 2 is ascii
    const SPALTEN:usize = 16;
    let mut command = String::new();

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

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

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

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

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

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

    getch();
    clear();

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

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

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

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

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

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

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

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => { // Button "h"
                    if cursorstate == 2 && cursorpos != 0 {cursorpos-=1}
                    else if cursorstate == 1 {cursorstate = 0}
                    else if cursorpos != 0 {cursorstate = 1; cursorpos-=1}
                },
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => { // Button "l"
                    if cursorstate == 2 && cursorpos != buffer.len()-1 {cursorpos+=1}
                    else if cursorstate == 0 {cursorstate = 1}
                    else if cursorstate == 1 && cursorpos != buffer.len()-1 {cursorstate = 0; cursorpos+=1}
                },
                 48 => { // Start of line is "to start"
                     cursorpos -= cursorpos%16;
                     if cursorstate == 1 {cursorstate = 0}
                },
                 36 => { //dollar is "to end"
                     if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)}
                        else {cursorpos = buffer.len()-1};
                    if cursorstate == 0 {cursorstate = 1;}
                },
                114 => mode = 1, //r replaces the next char
                120 => {buffer.remove(cursorpos);}, //x remove the next char
                105 => mode = 3, //i goes to insert mode
                 58 => {mode = 2; command.clear();}, // ":"
                 74 => {if cursorstate == 2 {cursorstate = 0;} else {cursorstate = 2;}}, // J jumps to ascii
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 && cursorstate == 2 { // r was pressed so replace next char in ascii mode
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8 },
                _ => (),
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 0 {
            match key { // Change left nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 55)<<4 },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 48)<<4 },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 1 {
            match key { // Change right nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55) },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48) },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); },
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        } else
        if mode == 3 {
            if cursorstate == 0 { // Left nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        {buffer.insert(cursorpos, (c as u8 - 55)<<4 ); cursorstate = 1;},
                    c @ 48... 57 => // 0-9
                        {buffer.insert(cursorpos, (c as u8 - 48)<<4); cursorstate = 1;},
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 1 { // Right nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55); cursorstate = 0; cursorpos+=1; },
                    c @ 48...57 => // 0-9
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48); cursorstate = 0; cursorpos+=1; },
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 2 { // Ascii
                match key {
                    c @ 32...126 => { buffer.insert(cursorpos, c as u8); },
                    27 => {mode = 0;},
                    _ => (),
                }
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);
    }

    refresh();
    endwin();
}

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

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

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s < buffer.len() {
                if z*spalten+s == cursorpos && cursorstate == 0 { attron(COLOR_PAIR(1)); } // Color of left nibble
                printw(&format!("{:01X}", buffer[z*spalten+s]>>4) ); // Display left nibble
                if z*spalten+s == cursorpos && cursorstate == 0 { attroff(COLOR_PAIR(1)); }

                if z*spalten+s == cursorpos && cursorstate == 1 { attron(COLOR_PAIR(1)); } // Color of right nibble
                printw(&format!("{:01X} ", buffer[z*spalten+s]&0x0F) ); // Display right nibble
                if z*spalten+s == cursorpos && cursorstate == 1 { attroff(COLOR_PAIR(1)); }
            } else {
                printw("-- ");
            }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos && cursorstate == 2 { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos && cursorstate == 2 { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
    for _ in 0 .. maxzeilen-zeilen-2 {
        printw("\n"); // Put the cursor on last line of terminal
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
    }
    if mode == 3 {
        printw("insert"); // Indicate that insert mode is active
    }
    printw(&format!("{}", command));
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate pnet;
extern crate time;

use getopts::Options;
use std::os;
use std::net::{IpAddr};
use std::str::FromStr;

use pnet::transport::TransportProtocol::{Ipv4};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::transport::TransportChannelType::{Layer3};
use pnet::transport::{transport_channel};
use pnet::old_packet::ipv4::{Ipv4Header, Ipv4Packet};

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    match matches.free.len() {
        0 => panic!("must provide ip address"),
        _ => {}
    }
    let addr_arg = matches.free[0].as_slice();
    let addr: IpAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();

    let protocol = Layer3(IpNextHeaderProtocols::Test1);
    let (mut icmp_sender, mut icmp_receiver) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel:
                        {}", e)
    };
    let packet = build_icmp_time_request_packet();
}

fn build_icmp_time_request_packet() -> IcmpRequestPacket {
    let mut packet = IcmpRequestPacket::new();
    packet
}

struct IcmpRequestPacket {
    ip_type: u8,
    ip_code: u8,
    ip_checksum: u16,
    identifier: u16,
    sequence: u16,
    originate_timestamp: u32,
    receive_timestamp: u32,
    transmit_timestamp: u32
}

impl IcmpRequestPacket {
    fn new() -> IcmpRequestPacket {
        let mut packet = IcmpRequestPacket {
            ip_type: 13,
            ip_code: 0,
            ip_checksum: 0,
            identifier: 0,
            sequence: 0,
            originate_timestamp: 0,
            receive_timestamp: 0,
            transmit_timestamp: 0
        };
        packet.set_originate_timestamp();
        // note we actually want to do this just before sending, not yet
        packet.set_checksum();
        packet
    }

    fn calculate_checksum(&self) -> u16 {
        // implement me
        let mut vec: Vec<u16> = vec![];
        vec.push((self.ip_type + self.ip_code) as u16);
        vec.push(self.ip_checksum);
        vec.push(self.identifier);
        vec.push(self.sequence);
        vec.push((self.originate_timestamp >> 16) as u16);
        vec.push(self.originate_timestamp as u16);
        vec.push((self.receive_timestamp >> 16) as u16);
        vec.push(self.receive_timestamp as u16);
        vec.push((self.transmit_timestamp >> 16) as u16);
        vec.push(self.transmit_timestamp as u16);
        let sum = vec.iter().fold(0u16, |sum, x| sum + *x);
        let ones_complement = 65535 - sum;
        ones_complement
    }

    fn set_checksum(&mut self) {
        self.ip_checksum = self.calculate_checksum();
    }

    fn set_originate_timestamp(&mut self) {
        self.originate_timestamp = time_after_utc();
    }
}

fn time_after_utc() -> u32 {
    let t = time::now_utc();
    let s: i32 = t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec;
    s as u32
}
<commit_msg>icmp checksum algo seems to work<commit_after>extern crate getopts;
extern crate pnet;
extern crate time;

use getopts::Options;
use std::os;
use std::net::{IpAddr};
use std::str::FromStr;

use pnet::transport::TransportProtocol::{Ipv4};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::transport::TransportChannelType::{Layer3};
use pnet::transport::{transport_channel};
use pnet::old_packet::ipv4::{Ipv4Header, Ipv4Packet};

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    match matches.free.len() {
        0 => panic!("must provide ip address"),
        _ => {}
    }
    let addr_arg = matches.free[0].as_slice();
    let addr: IpAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();

    let protocol = Layer3(IpNextHeaderProtocols::Test1);
    let (mut icmp_sender, mut icmp_receiver) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel:
                        {}", e)
    };
    let packet = build_icmp_time_request_packet();
}

fn build_icmp_time_request_packet() -> IcmpRequestPacket {
    let mut packet = IcmpRequestPacket::new();
    packet
}

struct IcmpRequestPacket {
    ip_type: u8,
    ip_code: u8,
    ip_checksum: u16,
    identifier: u16,
    sequence: u16,
    originate_timestamp: u32,
    receive_timestamp: u32,
    transmit_timestamp: u32
}

impl IcmpRequestPacket {
    fn new() -> IcmpRequestPacket {
        let mut packet = IcmpRequestPacket {
            ip_type: 13,
            ip_code: 0,
            ip_checksum: 0,
            identifier: 0,
            sequence: 0,
            originate_timestamp: 0,
            receive_timestamp: 0,
            transmit_timestamp: 0
        };
        packet.set_originate_timestamp();
        // note we actually want to do this just before sending, not yet
        packet.set_checksum();
        packet
    }

    fn calculate_checksum(&self) -> u16 {
        // implement me
        let mut vec: Vec<u16> = vec![];
        vec.push((self.ip_type + self.ip_code) as u16);
        vec.push(self.ip_checksum);
        vec.push(self.identifier);
        vec.push(self.sequence);
        vec.push((self.originate_timestamp >> 16) as u16);
        vec.push(self.originate_timestamp as u16);
        vec.push((self.receive_timestamp >> 16) as u16);
        vec.push(self.receive_timestamp as u16);
        vec.push((self.transmit_timestamp >> 16) as u16);
        vec.push(self.transmit_timestamp as u16);
        ones_complement_sum(vec.as_slice())
    }

    fn set_checksum(&mut self) {
        self.ip_checksum = self.calculate_checksum();
    }

    fn set_originate_timestamp(&mut self) {
        self.originate_timestamp = time_after_utc();
    }
}

fn time_after_utc() -> u32 {
    let t = time::now_utc();
    let s: i32 = t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec;
    s as u32
}

fn ones_complement_sum(sl: &[u16]) -> u16 {
    let sum = sl.iter().fold(0u32, |sum, x| sum + (*x as u32));
    65535 - (sum as u16) - (sum >> 16) as u16
}

mod tests {
    use super::ones_complement_sum;

    #[test]
    fn test_ones_complement_sum() {
        let mut vec: Vec<u16> = vec![];
        vec.push(0x4500);
        vec.push(0x003c);
        vec.push(0x1c46);
        vec.push(0x4000);
        vec.push(0x4006);
        vec.push(0x0000);
        vec.push(0xac10);
        vec.push(0x0a63);
        vec.push(0xac10);
        vec.push(0x0a0c);
        let r = vec.as_slice();
        assert_eq!(ones_complement_sum(r), 0xB1E6);
    }

    #[test]
    fn another_test_ones_complement_sum() {
        let mut vec: Vec<u16> = vec![];
        vec.push(0x0800);
        vec.push(0x0000);
        vec.push(0x0001);
        vec.push(0x1008);
        vec.push(0x6162);
        vec.push(0x6364);
        vec.push(0x6566);
        vec.push(0x6768);
        vec.push(0x696a);
        vec.push(0x6b6c);
        vec.push(0x6d6e);
        vec.push(0x6f70);
        vec.push(0x7172);
        vec.push(0x7374);
        vec.push(0x7576);
        vec.push(0x7761);
        vec.push(0x6263);
        vec.push(0x6465);
        vec.push(0x6667);
        vec.push(0x6869);
        let r = vec.as_slice();
        assert_eq!(ones_complement_sum(r), 15699);
    }
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::fs::File;
use std::io::Write;

#[derive(PartialEq, Clone)]
enum Ea {
    Any,
    Immidate,
    DataRegister,
    AddressRegister,
    Memory,
}

#[derive(PartialEq, Copy, Clone)]
enum Size {
    _Byte,
    Word,
    Long,
}

struct Op {
    name: &'static str,
    print_name: &'static str,
    count: usize,
    ea_type: Ea,
}

impl Op {
    fn new(name: &'static str, print_name: &'static str, count: usize, ea_type: Ea) -> Op {
        Op {
            name: name,
            print_name: print_name,
            count: count,
            ea_type: ea_type,
        }
    }
}

#[derive(Clone)]
struct CycleRule {
    word_count: usize,
    long_count: usize,
    source: Ea,
    dest: Ea,
}

impl CycleRule {
    fn new(word_count: usize, long_count: usize, source: Ea, dest: Ea) -> CycleRule {
        CycleRule {
            word_count: word_count,
            long_count: long_count,
            source: source,
            dest: dest,
        }
    }
}

struct Instruction<'a> {
    name: &'static str,
    cycle_rules: &'a [CycleRule],
}

fn calculate_cycle_count(inst: &Instruction, src: &Op, dest: &Op, size: Size) -> usize {
    let mut cycle_count = 0;

    for rule in inst.cycle_rules {
        if (src.ea_type == rule.source || rule.source == Ea::Any) &&
           (dest.ea_type == rule.dest || rule.dest == Ea::Any) {
            match size {
                Size::Word => cycle_count = rule.word_count,
                Size::Long => cycle_count = rule.long_count,
                _ => panic!("Not supported!"),
            }
            break;
        }
    }

    cycle_count += src.count + dest.count;

    if size == Size::Long {
        if src.count != 0 {
            cycle_count += 4;
        }

        if dest.count != 0 {
            cycle_count += 4;
        }
    }

    cycle_count
}

fn calc_cycle_count_one_op(inst: &Instruction, arg: &Op, size: Size) -> usize {
    let mut cycle_count = 0;

    for rule in inst.cycle_rules {
        if arg.ea_type == rule.dest || rule.dest == Ea::Any {
            match size {
                Size::Word => cycle_count = rule.word_count,
                Size::Long => cycle_count = rule.long_count,
                _ => panic!("Not supported!"),
            }
            break;
        }
    }

    cycle_count += arg.count;

    if size == Size::Long {
        if arg.count != 0 {
            cycle_count += 4;
        }
    }

    cycle_count
}

fn compile_statement(statement: &str) -> bool {
    let filename = "temp.s";

    {
        let mut file = File::create(filename).unwrap();
        write!(file, " {}", statement).unwrap();
    }

    let output =
        Command::new("vasmm68k_mot").arg(filename).output().expect("failed to execute process");

    output.status.success()
}

fn print_grid_table(name: &str, cycles: &Vec<Option<usize>>, src_table: &[Op], dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    for src in src_table {
        print!("| {name:<width$}", name = src.print_name, width = 9);

        for dest in dest_table {
            if let Some(cycle_count) = cycles[index] {
                print!("|{number:^width$}",
                       number = cycle_count,
                       width = dest.print_name.len() + 2);
            } else {
                print!("|{number:^width$}",
                       number = "*",
                       width = dest.print_name.len() + 2);
            }
            index += 1;
        }

        println!("|");
    }

    println!("");
}

fn print_table(name: &str, cycles: &Vec<Option<usize>>, dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    print!("| {name:<width$}", name = " ", width = 9);

    for dest in dest_table {
        if let Some(cycle_count) = cycles[index] {
            print!("|{number:^width$}",
                   number = cycle_count,
                   width = dest.print_name.len() + 2);
        } else {
            print!("|{number:^width$}",
                   number = "*",
                   width = dest.print_name.len() + 2);
        }
        index += 1;
    }

    println!("|");
    println!("");
}

fn generate_table(inst: &Instruction,
                  name: &str,
                  size: Size,
                  src_table: Option<&[Op]>,
                  dest_table: &[Op]) {
    let mut cycles = Vec::with_capacity(20 * 20);

    if let Some(src_opts) = src_table {
        for src in src_opts {
            for dst in dest_table {
                let statement = format!("{} {},{}", name, src.name, dst.name);
                if compile_statement(&statement) {
                    cycles.push(Some(calculate_cycle_count(inst, &src, &dst, size)));
                } else {
                    cycles.push(None);
                }
            }
        }

        print_grid_table(name, &cycles, &src_opts, dest_table);
    } else {
        for dst in dest_table {
            let statement = format!("{} {}", name, dst.name);
            if compile_statement(&statement) {
                cycles.push(Some(calc_cycle_count_one_op(inst, &dst, size)));
            } else {
                cycles.push(None);
            }
        }

        print_table(name, &cycles, dest_table);
    }
}

fn main() {
    let dest_types = [Op::new("d0", "Dn", 0, Ea::DataRegister),
                      Op::new("a0", "An", 0, Ea::AddressRegister),
                      Op::new("(a0)", "(An)", 4, Ea::Memory),
                      Op::new("(a0)+", "(An)+", 4, Ea::Memory),
                      Op::new("-(a0)", "-(An)", 6, Ea::Memory),
                      Op::new("2(a0)", "d(An)", 8, Ea::Memory),
                      Op::new("2(a0,d0)", "d(An,Dn)", 10, Ea::Memory),
                      Op::new("$4.W", "xxx.W", 8, Ea::Memory),
                      Op::new("$4.L", "xxx.L", 12, Ea::Memory)];

    let src_types = [Op::new("d0", "Dn", 0, Ea::DataRegister),
                     Op::new("a0", "An", 0, Ea::AddressRegister),
                     Op::new("(a0)", "(An)", 4, Ea::Memory),
                     Op::new("(a0)+", "(An)+", 4, Ea::Memory),
                     Op::new("-(a0)", "-(An)", 6, Ea::Memory),
                     Op::new("2(a0)", "d(An)", 8, Ea::Memory),
                     Op::new("2(a0,d0)", "d(An,Dn)", 10, Ea::Memory),
                     Op::new("$4.W", "xxx.W", 8, Ea::Memory),
                     Op::new("$4.L", "xxx.L", 12, Ea::Memory),
                     Op::new("2(pc)", "d(PC)", 8, Ea::Memory),
                     Op::new("2(pc,d0)", "d(PC,Dn)", 10, Ea::Memory),
                     Op::new("#2", "#xxx", 4, Ea::Immidate)];

    let inst_2_ops_000 = [
        Instruction {
                name: "abcd",
                cycle_rules: &[CycleRule::new(6, 6, Ea::DataRegister, Ea::DataRegister),
                                CycleRule::new(6, 10, Ea::Any, Ea::Any)],
        },
        Instruction {
                name: "add",
                cycle_rules: &[CycleRule::new(4, 8, Ea::Immidate, Ea::DataRegister),
                                CycleRule::new(8, 8, Ea::Immidate, Ea::Memory),
                                CycleRule::new(8, 8, Ea::Any, Ea::AddressRegister),
                                CycleRule::new(4, 6, Ea::Any, Ea::DataRegister),
                                CycleRule::new(8, 12, Ea::DataRegister, Ea::Memory)],
        },
        Instruction {
                name: "addq",
                cycle_rules: &[CycleRule::new(0, 0, Ea::Immidate, Ea::DataRegister),
                                CycleRule::new(4, 4, Ea::Immidate, Ea::Memory)],
        },
        Instruction {
                name: "addx",
                cycle_rules: &[CycleRule::new(4, 8, Ea::DataRegister, Ea::DataRegister),
                                CycleRule::new(6, 10, Ea::Any, Ea::Any)],
        },
    ];

    let inst_1_ops_000 = [Instruction {
                              name: "clr",
                              cycle_rules: &[CycleRule::new(4, 8, Ea::Any, Ea::Any)],
                          }];

    for inst in &inst_2_ops_000 {
        let name_long = format!("{}.l", inst.name);

        generate_table(&inst, inst.name, Size::Word, Some(&src_types), &dest_types);
        generate_table(&inst, &name_long, Size::Long, Some(&src_types), &dest_types);
    }

    // Generate instructions with one op

    for inst in &inst_1_ops_000 {
        let name_long = format!("{}.l", inst.name);

        generate_table(&inst, inst.name, Size::Word, None, &dest_types);
        generate_table(&inst, &name_long, Size::Long, None, &dest_types);
    }
}
<commit_msg>Added and/bchg<commit_after>use std::process::Command;
use std::fs::File;
use std::io::Write;

#[derive(PartialEq, Clone)]
enum Ea {
    Any,
    Immidate,
    DataRegister,
    AddressRegister,
    Memory,
}

#[derive(PartialEq, Copy, Clone)]
enum Size {
    _Byte,
    Word,
    Long,
}

struct Op {
    name: &'static str,
    print_name: &'static str,
    count: usize,
    ea_type: Ea,
}

impl Op {
    fn new(name: &'static str, print_name: &'static str, count: usize, ea_type: Ea) -> Op {
        Op {
            name: name,
            print_name: print_name,
            count: count,
            ea_type: ea_type,
        }
    }
}

#[derive(Clone)]
struct CycleRule {
    word_count: usize,
    long_count: usize,
    source: Ea,
    dest: Ea,
}

impl CycleRule {
    fn new(word_count: usize, long_count: usize, source: Ea, dest: Ea) -> CycleRule {
        CycleRule {
            word_count: word_count,
            long_count: long_count,
            source: source,
            dest: dest,
        }
    }
}

struct Instruction<'a> {
    name: &'static str,
    cycle_rules: &'a [CycleRule],
}

fn calculate_cycle_count(inst: &Instruction, src: &Op, dest: &Op, size: Size) -> usize {
    let mut cycle_count = 0;

    for rule in inst.cycle_rules {
        if (src.ea_type == rule.source || rule.source == Ea::Any) &&
           (dest.ea_type == rule.dest || rule.dest == Ea::Any) {
            match size {
                Size::Word => cycle_count = rule.word_count,
                Size::Long => cycle_count = rule.long_count,
                _ => panic!("Not supported!"),
            }
            break;
        }
    }

    cycle_count += src.count + dest.count;

    if size == Size::Long {
        if src.count != 0 {
            cycle_count += 4;
        }

        if dest.count != 0 {
            cycle_count += 4;
        }
    }

    cycle_count
}

fn calc_cycle_count_one_op(inst: &Instruction, arg: &Op, size: Size) -> usize {
    let mut cycle_count = 0;

    for rule in inst.cycle_rules {
        if arg.ea_type == rule.dest || rule.dest == Ea::Any {
            match size {
                Size::Word => cycle_count = rule.word_count,
                Size::Long => cycle_count = rule.long_count,
                _ => panic!("Not supported!"),
            }
            break;
        }
    }

    cycle_count += arg.count;

    if size == Size::Long {
        if arg.count != 0 {
            cycle_count += 4;
        }
    }

    cycle_count
}

fn compile_statement(statement: &str) -> bool {
    let filename = "temp.s";

    {
        let mut file = File::create(filename).unwrap();
        write!(file, " {}", statement).unwrap();
    }

    let output =
        Command::new("vasmm68k_mot").arg(filename).output().expect("failed to execute process");

    output.status.success()
}

fn print_grid_table(name: &str, cycles: &Vec<Option<usize>>, src_table: &[Op], dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    for src in src_table {
        print!("| {name:<width$}", name = src.print_name, width = 9);

        for dest in dest_table {
            if let Some(cycle_count) = cycles[index] {
                print!("|{number:^width$}",
                       number = cycle_count,
                       width = dest.print_name.len() + 2);
            } else {
                print!("|{number:^width$}",
                       number = "*",
                       width = dest.print_name.len() + 2);
            }
            index += 1;
        }

        println!("|");
    }

    println!("");
}

fn print_table(name: &str, cycles: &Vec<Option<usize>>, dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    print!("| {name:<width$}", name = " ", width = 9);

    for dest in dest_table {
        if let Some(cycle_count) = cycles[index] {
            print!("|{number:^width$}",
                   number = cycle_count,
                   width = dest.print_name.len() + 2);
        } else {
            print!("|{number:^width$}",
                   number = "*",
                   width = dest.print_name.len() + 2);
        }
        index += 1;
    }

    println!("|");
    println!("");
}

fn generate_table(inst: &Instruction,
                  name: &str,
                  size: Size,
                  src_table: Option<&[Op]>,
                  dest_table: &[Op]) {
    let mut cycles = Vec::with_capacity(20 * 20);

    if let Some(src_opts) = src_table {
        for src in src_opts {
            for dst in dest_table {
                let statement = format!("{} {},{}", name, src.name, dst.name);
                if compile_statement(&statement) {
                    cycles.push(Some(calculate_cycle_count(inst, &src, &dst, size)));
                } else {
                    cycles.push(None);
                }
            }
        }

        print_grid_table(name, &cycles, &src_opts, dest_table);
    } else {
        for dst in dest_table {
            let statement = format!("{} {}", name, dst.name);
            if compile_statement(&statement) {
                cycles.push(Some(calc_cycle_count_one_op(inst, &dst, size)));
            } else {
                cycles.push(None);
            }
        }

        print_table(name, &cycles, dest_table);
    }
}

fn main() {
    let dest_types = [Op::new("d0", "Dn", 0, Ea::DataRegister),
                      Op::new("a0", "An", 0, Ea::AddressRegister),
                      Op::new("(a0)", "(An)", 4, Ea::Memory),
                      Op::new("(a0)+", "(An)+", 4, Ea::Memory),
                      Op::new("-(a0)", "-(An)", 6, Ea::Memory),
                      Op::new("2(a0)", "d(An)", 8, Ea::Memory),
                      Op::new("2(a0,d0)", "d(An,Dn)", 10, Ea::Memory),
                      Op::new("$4.W", "xxx.W", 8, Ea::Memory),
                      Op::new("$4.L", "xxx.L", 12, Ea::Memory)];

    let src_types = [Op::new("d0", "Dn", 0, Ea::DataRegister),
                     Op::new("a0", "An", 0, Ea::AddressRegister),
                     Op::new("(a0)", "(An)", 4, Ea::Memory),
                     Op::new("(a0)+", "(An)+", 4, Ea::Memory),
                     Op::new("-(a0)", "-(An)", 6, Ea::Memory),
                     Op::new("2(a0)", "d(An)", 8, Ea::Memory),
                     Op::new("2(a0,d0)", "d(An,Dn)", 10, Ea::Memory),
                     Op::new("$4.W", "xxx.W", 8, Ea::Memory),
                     Op::new("$4.L", "xxx.L", 12, Ea::Memory),
                     Op::new("2(pc)", "d(PC)", 8, Ea::Memory),
                     Op::new("2(pc,d0)", "d(PC,Dn)", 10, Ea::Memory),
                     Op::new("#2", "#xxx", 4, Ea::Immidate)];

    let inst_2_ops_000 = [
        Instruction {
                name: "abcd",
                cycle_rules: &[CycleRule::new(6, 6, Ea::DataRegister, Ea::DataRegister),
                                CycleRule::new(6, 10, Ea::Any, Ea::Any)],
        },
        Instruction {
                name: "add",
                cycle_rules: &[CycleRule::new(4, 8, Ea::Immidate, Ea::DataRegister),
                                CycleRule::new(8, 8, Ea::Immidate, Ea::Memory),
                                CycleRule::new(8, 8, Ea::Any, Ea::AddressRegister),
                                CycleRule::new(4, 6, Ea::Any, Ea::DataRegister),
                                CycleRule::new(8, 12, Ea::DataRegister, Ea::Memory)],
        },
        Instruction {
                name: "addq",
                cycle_rules: &[CycleRule::new(0, 0, Ea::Immidate, Ea::DataRegister),
                                CycleRule::new(4, 4, Ea::Immidate, Ea::Memory)],
        },
        Instruction {
                name: "addx",
                cycle_rules: &[CycleRule::new(4, 8, Ea::DataRegister, Ea::DataRegister),
                                CycleRule::new(6, 10, Ea::Any, Ea::Any)],
        },
        Instruction {
                name: "and",
                cycle_rules: &[CycleRule::new(4, 8, Ea::Immidate, Ea::DataRegister),
                                CycleRule::new(8, 8, Ea::Immidate, Ea::Memory),
                                CycleRule::new(4, 6, Ea::Any, Ea::DataRegister),
                                CycleRule::new(8, 12, Ea::DataRegister, Ea::Memory)],
        },
        Instruction {
                name: "bchg",
                cycle_rules: &[CycleRule::new(8, 12, Ea::Immidate, Ea::Memory),
                                CycleRule::new(8, 8, Ea::DataRegister, Ea::Any)],
        },
    ];

    let inst_1_ops_000 = [Instruction {
                              name: "clr",
                              cycle_rules: &[CycleRule::new(4, 8, Ea::Any, Ea::Any)],
                          }];

    for inst in &inst_2_ops_000 {
        let name_long = format!("{}.l", inst.name);

        generate_table(&inst, inst.name, Size::Word, Some(&src_types), &dest_types);
        generate_table(&inst, &name_long, Size::Long, Some(&src_types), &dest_types);
    }

    // Generate instructions with one op

    for inst in &inst_1_ops_000 {
        let name_long = format!("{}.l", inst.name);

        generate_table(&inst, inst.name, Size::Word, None, &dest_types);
        generate_table(&inst, &name_long, Size::Long, None, &dest_types);
    }
}
<|endoftext|>