<commit_before>extern crate cryt;
extern crate clap;
extern crate regex;

use clap::{App, Arg, SubCommand};
use std::io::{self, Read, Write};
use regex::Regex;

use cryt::criteria::{self, BytesCriterion};
use cryt::encoding;
use cryt::xor::{self, Xor, KeysizeCriterion};

fn main() {
    let matches = App::new("cryt")
                          .version("v0.1")
                          .author("Nicolas Schmidt <nfschmidt@gmail.com>")
                          .about("Cryptographic tools")
                          .subcommand(SubCommand::with_name("encode")
                                      .about("Encode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Encode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Encode input in base64")))
                          .subcommand(SubCommand::with_name("decode")
                                      .about("Decode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Decode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Decode input in base64")))
                          .subcommand(SubCommand::with_name("encrypt")
                                      .about("Encrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Encrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("decrypt")
                                      .about("Decrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Decrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("attack")
                                      .about("Attack the specified encryption algorithm to decrypt the input")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Attack xor encrypted input")
                                                  .arg(Arg::with_name("criterion")
                                                       .short("c")
                                                       .long("criterion")
                                                       .takes_value(true)
                                                       .possible_values(&["printable", "text"])
                                                       .help("criterion to be used for scoring the results"))
                                                  .arg(Arg::with_name("detailed")
                                                       .short("d")
                                                       .long("detailed")
                                                       .required(false)
                                                       .help("Print decrypted result, the key and the score for the selected criterion"))
                                                  .subcommand(SubCommand::with_name("keysize")
                                                              .about("Determine the keysize of a repeated xor encryption")
                                                              .arg(Arg::with_name("criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))
                                                  .subcommand(SubCommand::with_name("repeated")
                                                              .about("Attack repeated xor encrypted input")
                                                              .arg(Arg::with_name("keysize-criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("k")
                                                                   .long("keysize-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("xor-criterion")
                                                                   .short("x")
                                                                   .long("xor-criterion")
                                                                   .takes_value(true)
                                                                   //.possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the intermediate block results"))
                                                              .arg(Arg::with_name("criterion")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("keysizes-try")
                                                                   .short("t")
                                                                   .long("keysizes-try")
                                                                   .takes_value(true)
                                                                   .required(false)
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("encode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_encode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_encode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("decode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_decode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_decode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("encrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_encrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("decrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_decrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("attack") {
        if let Some(xor_matches) = matches.subcommand_matches("xor") {
            if let Some(keysize_matches) = xor_matches.subcommand_matches("keysize") {
                let criterion = match keysize_matches.value_of("criterion") {
                    Some("hamming-distance") => Box::new(xor::hamming_distance_criterion),
                    Some(_) => Box::new(xor::hamming_distance_criterion),
                    None => Box::new(xor::hamming_distance_criterion),
                };

                let min = match keysize_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match keysize_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_keysize(criterion, min, max);
                return;
            } else if let Some(repeated_matches) = xor_matches.subcommand_matches("repeated") {
                let xor_criterion: Box<BytesCriterion> = match repeated_matches.value_of("xor-criterion") {
                    Some("printable") => Box::new(criteria::printable_bytes),
                    Some("text") => Box::new(criteria::text_bytes),
                    Some(value) => {
                        let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                        let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                        let byte = byte_text.parse::<u8>().unwrap();

                        if value.contains("byte(") {
                            criteria::make_common_byte(byte)
                        } else {
                            Box::new(criteria::text_bytes)
                        }
                    },
                    None => Box::new(criteria::text_bytes)
                };

                let result_criterion: Box<BytesCriterion> = match xor_matches.value_of("criterion") {
                    Some("printable") => Box::new(criteria::printable_bytes),
                    Some("text") => Box::new(criteria::text_bytes),
                    Some(value) => {
                        let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                        let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                        let byte = byte_text.parse::<u8>().unwrap();

                        if value.contains("byte(") {
                            criteria::make_common_byte(byte)
                        } else {
                            Box::new(criteria::text_bytes)
                        }
                    },
                    None => Box::new(criteria::text_bytes),
                };

                let keysize_criterion = match repeated_matches.value_of("keysize-criterion") {
                    Some("hamming-distance") => Box::new(xor::hamming_distance_criterion),
                    Some(_) => Box::new(xor::hamming_distance_criterion),
                    None => Box::new(xor::hamming_distance_criterion),
                };

                let keysize_try = match repeated_matches.value_of("keysizes-try") {
                    Some(v) => v.parse::<usize>().unwrap(),
                    None => 1
                };

                let min = match repeated_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match repeated_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_repeated(keysize_criterion, min, max, keysize_try, xor_criterion, result_criterion);
                return;
            }

            let criterion: Box<BytesCriterion> = match xor_matches.value_of("criterion") {
                Some("printable") => Box::new(criteria::printable_bytes),
                Some("text") => Box::new(criteria::text_bytes),
                Some(value) => {
                    let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                    let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                    let byte = byte_text.parse::<u8>().unwrap();

                    if value.contains("byte(") {
                        criteria::make_common_byte(byte)
                    } else {
                        Box::new(criteria::text_bytes)
                    }
                },
                None => Box::new(criteria::printable_bytes)
            };

            if xor_matches.is_present("detailed") {
                run_attack_xor_detailed(criterion);
            } else {
                run_attack_xor(criterion);
            }
        }
    } else {
        run_interpreter();
    }
}

fn run_encode_base64() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::base64_encode(&input);
    print!("{}", result);
}

fn run_encode_hex() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::hex_encode(&input);
    print!("{}", result);
}

fn run_decode_base64() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::base64_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_decode_hex() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::hex_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_encrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).encrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_decrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).decrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_attack_xor(criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (_, _, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(criterion)
        .result(&input);

    io::stdout().write(&decrypted).unwrap();
}

fn run_attack_xor_detailed(criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, score, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(criterion)
        .result(&input);

    print!("Key: {}\tScore: {}\tResult: ", key, score);
    io::stdout().write(&decrypted).unwrap();
    println!("");
}

fn run_attack_xor_keysize(criterion: Box<KeysizeCriterion>, min: u32, max: u32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let results = xor::KeysizeAttack::new()
        .with_min_length(min)
        .with_max_length(max)
        .with_criterion(criterion)
        .result(&input);

    for (size, score) in results {
        println!("Size: {}\tScore: {}", size, score);
    }
}

fn run_attack_xor_repeated(keysize_criterion: Box<KeysizeCriterion>, min: u32, max: u32, keysize_try: usize, xor_criterion: Box<BytesCriterion>, result_criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, decrypted) = xor::RepeatedAttack::new()
        .with_single_byte_attack(xor::SingleByteAttack::new()
                                 .with_criterion(xor_criterion))
        .with_keysize_attack(xor::KeysizeAttack::new()
                             .with_min_length(min)
                             .with_max_length(max)
                             .with_criterion(keysize_criterion))
        .with_result_criterion(result_criterion)
        .with_keysizes_count(keysize_try)
        .result(&input);

    let mut stdout = io::stdout();
    print!("Key: ");
    stdout.write(&key).unwrap();
    print!("\nDecrypted:\n");
    stdout.write(&decrypted).unwrap();
}

fn run_interpreter() {
    println!("Interpreter");
}
<commit_msg>Removed validation for xor-criterion<commit_after>extern crate cryt;
extern crate clap;
extern crate regex;

use clap::{App, Arg, SubCommand};
use std::io::{self, Read, Write};
use regex::Regex;

use cryt::criteria::{self, BytesCriterion};
use cryt::encoding;
use cryt::xor::{self, Xor, KeysizeCriterion};

fn main() {
    let matches = App::new("cryt")
                          .version("v0.1")
                          .author("Nicolas Schmidt <nfschmidt@gmail.com>")
                          .about("Cryptographic tools")
                          .subcommand(SubCommand::with_name("encode")
                                      .about("Encode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Encode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Encode input in base64")))
                          .subcommand(SubCommand::with_name("decode")
                                      .about("Decode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Decode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Decode input in base64")))
                          .subcommand(SubCommand::with_name("encrypt")
                                      .about("Encrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Encrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("decrypt")
                                      .about("Decrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Decrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("attack")
                                      .about("Attack the specified encryption algorithm to decrypt the input")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Attack xor encrypted input")
                                                  .arg(Arg::with_name("criterion")
                                                       .short("c")
                                                       .long("criterion")
                                                       .takes_value(true)
                                                       //.possible_values(&["printable", "text"])
                                                       .help("criterion to be used for scoring the results"))
                                                  .arg(Arg::with_name("detailed")
                                                       .short("d")
                                                       .long("detailed")
                                                       .required(false)
                                                       .help("Print decrypted result, the key and the score for the selected criterion"))
                                                  .subcommand(SubCommand::with_name("keysize")
                                                              .about("Determine the keysize of a repeated xor encryption")
                                                              .arg(Arg::with_name("criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))
                                                  .subcommand(SubCommand::with_name("repeated")
                                                              .about("Attack repeated xor encrypted input")
                                                              .arg(Arg::with_name("keysize-criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("k")
                                                                   .long("keysize-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("xor-criterion")
                                                                   .short("x")
                                                                   .long("xor-criterion")
                                                                   .takes_value(true)
                                                                   //.possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the intermediate block results"))
                                                              .arg(Arg::with_name("criterion")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   //.possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("keysizes-try")
                                                                   .short("t")
                                                                   .long("keysizes-try")
                                                                   .takes_value(true)
                                                                   .required(false)
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("encode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_encode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_encode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("decode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_decode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_decode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("encrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_encrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("decrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_decrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("attack") {
        if let Some(xor_matches) = matches.subcommand_matches("xor") {
            if let Some(keysize_matches) = xor_matches.subcommand_matches("keysize") {
                let criterion = match keysize_matches.value_of("criterion") {
                    Some("hamming-distance") => Box::new(xor::hamming_distance_criterion),
                    Some(_) => Box::new(xor::hamming_distance_criterion),
                    None => Box::new(xor::hamming_distance_criterion),
                };

                let min = match keysize_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match keysize_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_keysize(criterion, min, max);
                return;
            } else if let Some(repeated_matches) = xor_matches.subcommand_matches("repeated") {
                let xor_criterion: Box<BytesCriterion> = match repeated_matches.value_of("xor-criterion") {
                    Some("printable") => Box::new(criteria::printable_bytes),
                    Some("text") => Box::new(criteria::text_bytes),
                    Some(value) => {
                        let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                        let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                        let byte = byte_text.parse::<u8>().unwrap();

                        if value.contains("byte(") {
                            criteria::make_common_byte(byte)
                        } else {
                            Box::new(criteria::text_bytes)
                        }
                    },
                    None => Box::new(criteria::text_bytes)
                };

                let result_criterion: Box<BytesCriterion> = match xor_matches.value_of("criterion") {
                    Some("printable") => Box::new(criteria::printable_bytes),
                    Some("text") => Box::new(criteria::text_bytes),
                    Some(value) => {
                        let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                        let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                        let byte = byte_text.parse::<u8>().unwrap();

                        if value.contains("byte(") {
                            criteria::make_common_byte(byte)
                        } else {
                            Box::new(criteria::text_bytes)
                        }
                    },
                    None => Box::new(criteria::text_bytes),
                };

                let keysize_criterion = match repeated_matches.value_of("keysize-criterion") {
                    Some("hamming-distance") => Box::new(xor::hamming_distance_criterion),
                    Some(_) => Box::new(xor::hamming_distance_criterion),
                    None => Box::new(xor::hamming_distance_criterion),
                };

                let keysize_try = match repeated_matches.value_of("keysizes-try") {
                    Some(v) => v.parse::<usize>().unwrap(),
                    None => 1
                };

                let min = match repeated_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match repeated_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_repeated(keysize_criterion, min, max, keysize_try, xor_criterion, result_criterion);
                return;
            }

            let criterion: Box<BytesCriterion> = match xor_matches.value_of("criterion") {
                Some("printable") => Box::new(criteria::printable_bytes),
                Some("text") => Box::new(criteria::text_bytes),
                Some(value) => {
                    let re = Regex::new(r"byte\((\d{1,3})\)").unwrap();
                    let byte_text = re.captures(value).unwrap().get(1).unwrap().as_str();
                    let byte = byte_text.parse::<u8>().unwrap();

                    if value.contains("byte(") {
                        criteria::make_common_byte(byte)
                    } else {
                        Box::new(criteria::text_bytes)
                    }
                },
                None => Box::new(criteria::printable_bytes)
            };

            if xor_matches.is_present("detailed") {
                run_attack_xor_detailed(criterion);
            } else {
                run_attack_xor(criterion);
            }
        }
    } else {
        run_interpreter();
    }
}

fn run_encode_base64() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::base64_encode(&input);
    print!("{}", result);
}

fn run_encode_hex() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::hex_encode(&input);
    print!("{}", result);
}

fn run_decode_base64() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::base64_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_decode_hex() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::hex_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_encrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).encrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_decrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).decrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_attack_xor(criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (_, _, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(criterion)
        .result(&input);

    io::stdout().write(&decrypted).unwrap();
}

fn run_attack_xor_detailed(criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, score, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(criterion)
        .result(&input);

    print!("Key: {}\tScore: {}\tResult: ", key, score);
    io::stdout().write(&decrypted).unwrap();
    println!("");
}

fn run_attack_xor_keysize(criterion: Box<KeysizeCriterion>, min: u32, max: u32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let results = xor::KeysizeAttack::new()
        .with_min_length(min)
        .with_max_length(max)
        .with_criterion(criterion)
        .result(&input);

    for (size, score) in results {
        println!("Size: {}\tScore: {}", size, score);
    }
}

fn run_attack_xor_repeated(keysize_criterion: Box<KeysizeCriterion>, min: u32, max: u32, keysize_try: usize, xor_criterion: Box<BytesCriterion>, result_criterion: Box<BytesCriterion>) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, decrypted) = xor::RepeatedAttack::new()
        .with_single_byte_attack(xor::SingleByteAttack::new()
                                 .with_criterion(xor_criterion))
        .with_keysize_attack(xor::KeysizeAttack::new()
                             .with_min_length(min)
                             .with_max_length(max)
                             .with_criterion(keysize_criterion))
        .with_result_criterion(result_criterion)
        .with_keysizes_count(keysize_try)
        .result(&input);

    let mut stdout = io::stdout();
    print!("Key: ");
    stdout.write(&key).unwrap();
    print!("\nDecrypted:\n");
    stdout.write(&decrypted).unwrap();
}

fn run_interpreter() {
    println!("Interpreter");
}
<|endoftext|>


<commit_before>// Bitcoin secp256k1 bindings
// Written in 2019 by
//   Elichai Turkel
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # secp256k1 no-std test.
//! This binary is a short smallest rust code to produce a working binary *without libstd*.
//! This gives us 2 things:
//!     1. Test that the parts of the code that should work in a no-std enviroment actually work.
//!     2. Test that we don't accidentally import libstd into `secp256k1`.
//!
//! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`.
//! (Making sure that it successfully printed that. i.e. it didn't abort before that).
//!
//! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error:
//! `error[E0152]: duplicate lang item found`.
//! Example:
//! ```
//! error[E0152]: duplicate lang item found: `eh_personality`.
//!   --> src/main.rs:37:1
//!    |
//! 37 | pub extern "C" fn rust_eh_personality() {}
//!    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!    |
//!    = note: first defined in crate `panic_unwind` (which `std` depends on).
//! ```
//!
//! Notes:
//!     * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493
//!     * Requires linking with `libc` for calling `printf`.
//!

#![feature(lang_items)]
#![feature(start)]
#![feature(core_intrinsics)]
#![feature(panic_info_message)]
#![no_std]
extern crate libc;
extern crate secp256k1;
extern crate serde_cbor;

use core::fmt::{self, write, Write};
use core::intrinsics;
use core::panic::PanicInfo;

use secp256k1::rand::{self, RngCore};
use secp256k1::serde::Serialize;
use secp256k1::*;
use secp256k1::ecdh::SharedSecret;

use serde_cbor::de;
use serde_cbor::ser::SliceWrite;
use serde_cbor::Serializer;

struct FakeRng;
impl RngCore for FakeRng {
    fn next_u32(&mut self) -> u32 {
        57
    }
    fn next_u64(&mut self) -> u64 {
        57
    }
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        for i in dest {
            *i = 57;
        }
        Ok(())
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.try_fill_bytes(dest).unwrap();
    }
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    let mut buf = [0u8; 600_000];
    let size = Secp256k1::preallocate_size();
    unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };

    let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
    secp.randomize(&mut FakeRng);
    let secret_key = SecretKey::new(&mut FakeRng);
    let public_key = PublicKey::from_secret_key(&secp, &secret_key);
    let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");

    let sig = secp.sign(&message, &secret_key);
    assert!(secp.verify(&message, &sig, &public_key).is_ok());

    let mut cbor_ser = [0u8; 100];
    let writer = SliceWrite::new(&mut cbor_ser[..]);
    let mut ser = Serializer::new(writer);
    sig.serialize(&mut ser).unwrap();
    let size = ser.into_inner().bytes_written();
    let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap();
    assert_eq!(sig, new_sig);

    let _ = SharedSecret::new(&public_key, &secret_key);
    let mut x_arr = [0u8; 32];
    let y_arr = unsafe { SharedSecret::new_with_hash_no_panic(&public_key, &secret_key, |x,y| {
        x_arr = x;
        y.into()
    })}.unwrap();
    assert_ne!(x_arr, [0u8; 32]);
    assert_ne!(&y_arr[..], &[0u8; 32][..]);

    unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) };
    0
}

// These functions are used by the compiler, but not
// for a bare-bones hello world. These are normally
// provided by libstd.
#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

// This function may be needed based on the compilation target.
#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

const MAX_PRINT: usize = 511;
struct Print {
    loc: usize,
    buf: [u8; 512],
}

impl Print {
    pub fn new() -> Self {
        Self {
            loc: 0,
            buf: [0u8; 512],
        }
    }

    pub fn print(&self) {
        unsafe {
            let newline = "\n";
            libc::printf(self.buf.as_ptr() as _);
            libc::printf(newline.as_ptr() as _);
        }
    }
}

impl Write for Print {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        let curr = self.loc;
        if curr + s.len() > MAX_PRINT {
            unsafe {
                libc::printf("overflow\n\0".as_ptr() as _);
                intrinsics::abort();
            }
        }
        self.loc += s.len();
        self.buf[curr..self.loc].copy_from_slice(s.as_bytes());
        Ok(())
    }
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    unsafe { libc::printf("shi1\n\0".as_ptr() as _) };
    let msg = info.message().unwrap();
    let mut buf = Print::new();
    write(&mut buf, *msg).unwrap();
    buf.print();
    unsafe { intrinsics::abort() }
}
<commit_msg>Fix no-std raw test, after removal of lang items<commit_after>// Bitcoin secp256k1 bindings
// Written in 2019 by
//   Elichai Turkel
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # secp256k1 no-std test.
//! This binary is a short smallest rust code to produce a working binary *without libstd*.
//! This gives us 2 things:
//!     1. Test that the parts of the code that should work in a no-std enviroment actually work.
//!     2. Test that we don't accidentally import libstd into `secp256k1`.
//!
//! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`.
//! (Making sure that it successfully printed that. i.e. it didn't abort before that).
//!
//! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error:
//! `error[E0152]: duplicate lang item found`.
//! Example:
//! ```
//! error[E0152]: duplicate lang item found: `eh_personality`.
//!   --> src/main.rs:37:1
//!    |
//! 37 | pub extern "C" fn rust_eh_personality() {}
//!    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!    |
//!    = note: first defined in crate `panic_unwind` (which `std` depends on).
//! ```
//!
//! Notes:
//!     * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493
//!     * Requires linking with `libc` for calling `printf`.
//!

#![feature(lang_items)]
#![feature(start)]
#![feature(core_intrinsics)]
#![feature(panic_info_message)]
#![no_std]
extern crate libc;
extern crate secp256k1;
extern crate serde_cbor;

use core::fmt::{self, write, Write};
use core::intrinsics;
use core::panic::PanicInfo;

use secp256k1::ecdh::SharedSecret;
use secp256k1::rand::{self, RngCore};
use secp256k1::serde::Serialize;
use secp256k1::*;

use serde_cbor::de;
use serde_cbor::ser::SliceWrite;
use serde_cbor::Serializer;

struct FakeRng;
impl RngCore for FakeRng {
    fn next_u32(&mut self) -> u32 {
        57
    }
    fn next_u64(&mut self) -> u64 {
        57
    }
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        for i in dest {
            *i = 57;
        }
        Ok(())
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.try_fill_bytes(dest).unwrap();
    }
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    let mut buf = [0u8; 600_000];
    let size = Secp256k1::preallocate_size();
    unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };

    let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
    secp.randomize(&mut FakeRng);
    let secret_key = SecretKey::new(&mut FakeRng);
    let public_key = PublicKey::from_secret_key(&secp, &secret_key);
    let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");

    let sig = secp.sign(&message, &secret_key);
    assert!(secp.verify(&message, &sig, &public_key).is_ok());

    let mut cbor_ser = [0u8; 100];
    let writer = SliceWrite::new(&mut cbor_ser[..]);
    let mut ser = Serializer::new(writer);
    sig.serialize(&mut ser).unwrap();
    let size = ser.into_inner().bytes_written();
    let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap();
    assert_eq!(sig, new_sig);

    let _ = SharedSecret::new(&public_key, &secret_key);
    let mut x_arr = [0u8; 32];
    let y_arr = unsafe { SharedSecret::new_with_hash_no_panic(&public_key, &secret_key, |x,y| {
        x_arr = x;
        y.into()
    })}.unwrap();
    assert_ne!(x_arr, [0u8; 32]);
    assert_ne!(&y_arr[..], &[0u8; 32][..]);

    unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) };
    0
}

const MAX_PRINT: usize = 511;
struct Print {
    loc: usize,
    buf: [u8; 512],
}

impl Print {
    pub fn new() -> Self {
        Self {
            loc: 0,
            buf: [0u8; 512],
        }
    }

    pub fn print(&self) {
        unsafe {
            let newline = "\n";
            libc::printf(self.buf.as_ptr() as _);
            libc::printf(newline.as_ptr() as _);
        }
    }
}

impl Write for Print {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        let curr = self.loc;
        if curr + s.len() > MAX_PRINT {
            unsafe {
                libc::printf("overflow\n\0".as_ptr() as _);
                intrinsics::abort();
            }
        }
        self.loc += s.len();
        self.buf[curr..self.loc].copy_from_slice(s.as_bytes());
        Ok(())
    }
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    unsafe { libc::printf("shi1\n\0".as_ptr() as _) };
    let msg = info.message().unwrap();
    let mut buf = Print::new();
    write(&mut buf, *msg).unwrap();
    buf.print();
    unsafe { intrinsics::abort() }
}
<|endoftext|>


<commit_before>// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

use async_trait::async_trait;
use kvproto::coprocessor::{KeyRange, Response};
use protobuf::Message;
use tidb_query_common::storage::scanner::{RangesScanner, RangesScannerOptions};
use tidb_query_common::storage::Range;
use tipb::{ChecksumAlgorithm, ChecksumRequest, ChecksumResponse};

use crate::coprocessor::dag::TiKVStorage;
use crate::coprocessor::*;
use crate::storage::{Snapshot, SnapshotStore, Statistics};

// `ChecksumContext` is used to handle `ChecksumRequest`
pub struct ChecksumContext<S: Snapshot> {
    req: ChecksumRequest,
    scanner: RangesScanner<TiKVStorage<SnapshotStore<S>>>,
}

impl<S: Snapshot> ChecksumContext<S> {
    pub fn new(
        req: ChecksumRequest,
        ranges: Vec<KeyRange>,
        start_ts: u64,
        snap: S,
        req_ctx: &ReqContext,
    ) -> Result<Self> {
        let store = SnapshotStore::new(
            snap,
            start_ts.into(),
            req_ctx.context.get_isolation_level(),
            !req_ctx.context.get_not_fill_cache(),
            req_ctx.bypass_locks.clone(),
            false,
        );
        let scanner = RangesScanner::new(RangesScannerOptions {
            storage: TiKVStorage::new(store, false),
            ranges: ranges
                .into_iter()
                .map(|r| Range::from_pb_range(r, false))
                .collect(),
            scan_backward_in_range: false,
            is_key_only: false,
            is_scanned_range_aware: false,
        });
        Ok(Self { req, scanner })
    }
}

#[async_trait]
impl<S: Snapshot> RequestHandler for ChecksumContext<S> {
    async fn handle_request(&mut self) -> Result<Response> {
        let algorithm = self.req.get_algorithm();
        if algorithm != ChecksumAlgorithm::Crc64Xor {
            return Err(box_err!("unknown checksum algorithm {:?}", algorithm));
        }

        let mut checksum = 0;
        let mut total_kvs = 0;
        let mut total_bytes = 0;
        let (old_prefix, new_prefix) = if self.req.has_rule() {
            let mut rule = self.req.get_rule().clone();
            (rule.take_old_prefix(), rule.take_new_prefix())
        } else {
            (vec![], vec![])
        };

        let mut prefix_digest = crc64fast::Digest::new();
        prefix_digest.write(&old_prefix);

        while let Some((k, v)) = self.scanner.next()? {
            if !k.starts_with(&new_prefix) {
                return Err(box_err!("Wrong prefix expect: {:?}", new_prefix));
            }
            checksum =
                checksum_crc64_xor(checksum, prefix_digest.clone(), &k[new_prefix.len()..], &v);
            total_kvs += 1;
            total_bytes += k.len() + v.len() + old_prefix.len() - new_prefix.len();
        }

        let mut resp = ChecksumResponse::default();
        resp.set_checksum(checksum);
        resp.set_total_kvs(total_kvs);
        resp.set_total_bytes(total_bytes as u64);
        let data = box_try!(resp.write_to_bytes());

        let mut resp = Response::default();
        resp.set_data(data);
        Ok(resp)
    }

    fn collect_scan_statistics(&mut self, dest: &mut Statistics) {
        self.scanner.collect_storage_stats(dest)
    }
}

pub fn checksum_crc64_xor(
    checksum: u64,
    mut digest: crc64fast::Digest,
    k_suffix: &[u8],
    v: &[u8],
) -> u64 {
    digest.write(k_suffix);
    digest.write(v);
    checksum ^ digest.sum64()
}
<commit_msg>copr: allow checksum request to be rescheduled (#9094)<commit_after>// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

use std::time::Instant;

use async_trait::async_trait;
use kvproto::coprocessor::{KeyRange, Response};
use protobuf::Message;
use tidb_query_common::storage::scanner::{RangesScanner, RangesScannerOptions};
use tidb_query_common::storage::Range;
use tidb_query_executors::runner::MAX_TIME_SLICE;
use tidb_query_expr::BATCH_MAX_SIZE;
use tipb::{ChecksumAlgorithm, ChecksumRequest, ChecksumResponse};
use yatp::task::future::reschedule;

use crate::coprocessor::dag::TiKVStorage;
use crate::coprocessor::*;
use crate::storage::{Snapshot, SnapshotStore, Statistics};

// `ChecksumContext` is used to handle `ChecksumRequest`
pub struct ChecksumContext<S: Snapshot> {
    req: ChecksumRequest,
    scanner: RangesScanner<TiKVStorage<SnapshotStore<S>>>,
}

impl<S: Snapshot> ChecksumContext<S> {
    pub fn new(
        req: ChecksumRequest,
        ranges: Vec<KeyRange>,
        start_ts: u64,
        snap: S,
        req_ctx: &ReqContext,
    ) -> Result<Self> {
        let store = SnapshotStore::new(
            snap,
            start_ts.into(),
            req_ctx.context.get_isolation_level(),
            !req_ctx.context.get_not_fill_cache(),
            req_ctx.bypass_locks.clone(),
            false,
        );
        let scanner = RangesScanner::new(RangesScannerOptions {
            storage: TiKVStorage::new(store, false),
            ranges: ranges
                .into_iter()
                .map(|r| Range::from_pb_range(r, false))
                .collect(),
            scan_backward_in_range: false,
            is_key_only: false,
            is_scanned_range_aware: false,
        });
        Ok(Self { req, scanner })
    }
}

#[async_trait]
impl<S: Snapshot> RequestHandler for ChecksumContext<S> {
    async fn handle_request(&mut self) -> Result<Response> {
        let algorithm = self.req.get_algorithm();
        if algorithm != ChecksumAlgorithm::Crc64Xor {
            return Err(box_err!("unknown checksum algorithm {:?}", algorithm));
        }

        let mut checksum = 0;
        let mut total_kvs = 0;
        let mut total_bytes = 0;
        let (old_prefix, new_prefix) = if self.req.has_rule() {
            let mut rule = self.req.get_rule().clone();
            (rule.take_old_prefix(), rule.take_new_prefix())
        } else {
            (vec![], vec![])
        };

        let mut prefix_digest = crc64fast::Digest::new();
        prefix_digest.write(&old_prefix);

        let mut row_count = 0;
        let mut time_slice_start = Instant::now();
        while let Some((k, v)) = self.scanner.next()? {
            row_count += 1;
            if row_count >= BATCH_MAX_SIZE {
                if time_slice_start.elapsed() > MAX_TIME_SLICE {
                    reschedule().await;
                    time_slice_start = Instant::now();
                }
                row_count = 0;
            }

            if !k.starts_with(&new_prefix) {
                return Err(box_err!("Wrong prefix expect: {:?}", new_prefix));
            }
            checksum =
                checksum_crc64_xor(checksum, prefix_digest.clone(), &k[new_prefix.len()..], &v);
            total_kvs += 1;
            total_bytes += k.len() + v.len() + old_prefix.len() - new_prefix.len();
        }

        let mut resp = ChecksumResponse::default();
        resp.set_checksum(checksum);
        resp.set_total_kvs(total_kvs);
        resp.set_total_bytes(total_bytes as u64);
        let data = box_try!(resp.write_to_bytes());

        let mut resp = Response::default();
        resp.set_data(data);
        Ok(resp)
    }

    fn collect_scan_statistics(&mut self, dest: &mut Statistics) {
        self.scanner.collect_storage_stats(dest)
    }
}

pub fn checksum_crc64_xor(
    checksum: u64,
    mut digest: crc64fast::Digest,
    k_suffix: &[u8],
    v: &[u8],
) -> u64 {
    digest.write(k_suffix);
    digest.write(v);
    checksum ^ digest.sum64()
}
<|endoftext|>


<commit_before>use nom::{
  IResult,
  bytes::complete::{
      tag,
      take_while1,
  },
  multi::{
      separated_list0,
      many0,
  },
  combinator::map,
  branch::alt,
  sequence::delimited,
  character::complete::{
      multispace1,
      multispace0,
  },
};
use std::fmt;

// TODO: add in specific support for parsing at-here and other special <!user_id> entities
// so that in the message subsystem it can do the right thing
#[derive(Debug, PartialEq)]
pub struct Command<'a> (pub &'a str, pub Vec<&'a str>);

pub fn command(input: &str) -> IResult<&str, Command> {
    let (input, _) = tag("!")(input)?;
    let (input, cmd) = command_string(input)?;
    let (input, _) = multispace0(input)?;
    let (input, arg) = args(input)?;

    Ok((input, Command(cmd, arg)))
}

fn command_string(input: &str) -> IResult<&str, &str> {
    take_while1(|c:char| c.is_alphanumeric())(input)
}

fn not_multispace1(input: &str) -> IResult<&str, &str> {
    alt((
        delimited(
            tag("\""),
            take_while1(|c:char| c != '"'),
            tag("\""),
        ),
        take_while1(|c:char| !c.is_whitespace()),
    ))(input)
}

fn args(input: &str) -> IResult<&str, Vec<&str>> {
    separated_list0(multispace1, not_multispace1)(input)
}

// This starts karma tokenizer stream
// TODO: make fancy but for now basic objective
// 1. Convert inbound &str to a token stream and work on that (for structural concerns)
// 2. support 2 types of braces '"' and '['/']' to support single type and open/close
// 3. suppport 3 types of karma, up, down, side
// 4. support single character or multiple character karma token, for now '++', '--', '+-' '±'
// 5. support 'emoji' karma token - ':++:'
#[derive(Debug, PartialEq, Clone)]
enum KarmaToken<'a>{
    // Alphanumberic
    Text(&'a str),
    // Whitespace (with a copy in so we can reproduce the string exactly) (only because we care if
    // there is whitespace before the karma or not)
    Space(&'a str),
    // Only the " mark
    Quote,
    // Only the [ mark
    OpenBrace,
    // Only the ] mark
    CloseBrace,
    // Karma (only complete chunk makes it in) (':++:', '++', '--', '+-', '±' for now)
    // TODO: this is eager, will need to figure out if we want to deal with "+++" at all
    Karma(&'a str)
}

impl <'a> fmt::Display for KarmaToken<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KarmaToken::Text(t)    => write!(f, "{}", t),
            KarmaToken::Space(t)   => write!(f, "{}", t),
            KarmaToken::Karma(t)   => write!(f, "{}", t),
            KarmaToken::Quote      => write!(f, "\""),
            KarmaToken::OpenBrace  => write!(f, "["),
            KarmaToken::CloseBrace => write!(f, "]"),
        }
    }
}

fn all_token(input: &str) -> IResult<&str, Vec<KarmaToken>> {
    many0(token)(input)
}

fn token(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        map(tag(":++:"), |k| KarmaToken::Karma(k)),
        map(tag("++"),   |k| KarmaToken::Karma(k)),
        map(tag("--"),   |k| KarmaToken::Karma(k)),
        map(tag("+-"),   |k| KarmaToken::Karma(k)),
        map(tag("±"),    |k| KarmaToken::Karma(k)),
        map(tag("\""),   |_| KarmaToken::Quote),
        map(tag("["),    |_| KarmaToken::OpenBrace),
        map(tag("]"),    |_| KarmaToken::CloseBrace),
        space,
        text,
    ))(input)
}

fn space(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_whitespace()), |s| KarmaToken::Space(s))(input)
}

fn text(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_alphanumeric()), |s| KarmaToken::Text(s))(input)
}

// Now here begins the actual structural karma parser

#[cfg(test)]
mod test_structural_karma {
    use super::*;

    #[test]
    fn test_karma() {
    }
}

#[cfg(test)]
mod test_karma_token {
    use super::*;

    #[test]
    fn test_karma() {
        let karma = vec![":++:", "++", "--", "+-", "±"];

        assert_eq!(
            all_token(&karma.join("")),
            Ok(("", karma.iter().map(|k| KarmaToken::Karma(k)).collect()))
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(
            all_token("\"\"\""),
            Ok(("", vec![KarmaToken::Quote; 3]))
        );
    }

    #[test]
    fn test_brace() {
        assert_eq!(
            all_token("][]]"),
            Ok(("", vec![
                KarmaToken::CloseBrace,
                KarmaToken::OpenBrace,
                KarmaToken::CloseBrace,
                KarmaToken::CloseBrace
            ]))
        );
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(
            all_token(" \t"),
            Ok(("", vec![KarmaToken::Space(" \t")]))
        );
    }

    #[test]
    fn test_whitespace_block() {
        assert_eq!(
            all_token(" ]\t[  "),
            Ok(("", vec![
                KarmaToken::Space(" "),
                KarmaToken::CloseBrace,
                KarmaToken::Space("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Space("  ")
            ]))
        );
    }

    #[test]
    fn test_text() {
        assert_eq!(
            all_token("this藏test"),
            Ok(("", vec![KarmaToken::Text("this藏test")]))
        );
    }

    #[test]
    fn test_text_block() {
        assert_eq!(
            all_token("this\t[\"藏  ++]"),
            Ok(("", vec![
                KarmaToken::Text("this"),
                KarmaToken::Space("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Quote,
                KarmaToken::Text("藏"),
                KarmaToken::Space("  "),
                KarmaToken::Karma("++"),
                KarmaToken::CloseBrace,
            ]))
        );
    }

    #[test]
    fn test_roundtrip() {
        let text = "this\t[\"藏  ++]";
        let parse = all_token(text).unwrap().1;

        assert_eq!(parse.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(""), text);
    }
}

#[cfg(test)]
mod test_command {
    use super::*;

    #[test]
    fn test_command() {
        assert_eq!(
            command("!karma"),
            Ok(("", Command("karma", vec![])))
        );
    }

    #[test]
    fn test_command_one_arg() {
        assert_eq!(
            command("!karma a"),
            Ok(("", Command("karma", vec!["a"])))
        );
    }

    #[test]
    fn test_command_two_arg() {
        assert_eq!(
            command("!karma a bcd"),
            Ok(("", Command("karma", vec!["a", "bcd"])))
        );
    }

    #[test]
    fn test_command_one_quote_arg() {
        assert_eq!(
            command("!karma \"a b\""),
            Ok(("", Command("karma", vec!["a b"])))
        );
    }

    #[test]
    fn test_command_two_quote_arg() {
        assert_eq!(
            command("!karma \"a b\" \"c d\""),
            Ok(("", Command("karma", vec!["a b", "c d"])))
        );
    }

    #[test]
    fn test_command_two_quote_and_unquote_arg() {
        assert_eq!(
            command("!karma \"a b\" cd"),
            Ok(("", Command("karma", vec!["a b", "cd"])))
        );
    }

    #[test]
    fn test_command_fail_close_arg() {
        assert_eq!(
            command("!karma \"a"),
            Ok(("", Command("karma", vec!["\"a"])))
        );
    }

    #[test]
    fn test_command_fail_open_arg() {
        assert_eq!(
            command("!karma a\""),
            Ok(("", Command("karma", vec!["a\""])))
        );
    }

    // TODO: decide how to deal with this case
//    #[test]
//    fn test_command_multi_quote_arg() {
//        assert_eq!(
//            command("!karma \"\"a\"\""),
//            Ok(("", Command("karma", vec!["a"])))
//        );
//    }

    // TODO: decide how to deal with this case
//    #[test]
//    fn test_command_escape_quote_arg() {
//        assert_eq!(
//            command("!karma \"\\\"a\\\"\""),
//            Ok(("", Command("karma", vec!["\"a\""])))
//        );
//    }
}
<commit_msg>Did a heck ton of code for a custom tokens type, not sure if we will stick with this but let's try this out for now<commit_after>use nom::{
  IResult,
  bytes::complete::{
      tag,
      take_while1,
  },
  multi::{
      separated_list0,
      many0,
  },
  combinator::map,
  branch::alt,
  sequence::delimited,
  character::complete::{
      multispace1,
      multispace0,
  },
};
use std::fmt;

use nom::InputLength;
use nom::InputTake;
use nom::InputIter;
use nom::Slice;
use nom::UnspecializedInput;

use std::iter::Enumerate;
use std::ops::RangeFull;
use std::ops::RangeFrom;
use std::ops::RangeTo;
use std::ops::Range;

// TODO: add in specific support for parsing at-here and other special <!user_id> entities
// so that in the message subsystem it can do the right thing
#[derive(Debug, PartialEq)]
pub struct Command<'a> (pub &'a str, pub Vec<&'a str>);

pub fn command(input: &str) -> IResult<&str, Command> {
    let (input, _) = tag("!")(input)?;
    let (input, cmd) = command_string(input)?;
    let (input, _) = multispace0(input)?;
    let (input, arg) = args(input)?;

    Ok((input, Command(cmd, arg)))
}

fn command_string(input: &str) -> IResult<&str, &str> {
    take_while1(|c:char| c.is_alphanumeric())(input)
}

fn not_multispace1(input: &str) -> IResult<&str, &str> {
    alt((
        delimited(
            tag("\""),
            take_while1(|c:char| c != '"'),
            tag("\""),
        ),
        take_while1(|c:char| !c.is_whitespace()),
    ))(input)
}

fn args(input: &str) -> IResult<&str, Vec<&str>> {
    separated_list0(multispace1, not_multispace1)(input)
}

// This starts karma tokenizer stream
// TODO: make fancy but for now basic objective
// 1. Convert inbound &str to a token stream and work on that (for structural concerns)
// 2. support 2 types of braces '"' and '['/']' to support single type and open/close
// 3. suppport 3 types of karma, up, down, side
// 4. support single character or multiple character karma token, for now '++', '--', '+-' '±'
// 5. support 'emoji' karma token - ':++:'
#[derive(Debug, PartialEq, Clone)]
enum KarmaToken<'a>{
    // Alphanumberic
    Text(&'a str),
    // Whitespace (with a copy in so we can reproduce the string exactly) (only because we care if
    // there is whitespace before the karma or not)
    Space(&'a str),
    // Only the " mark
    // TODO: quotation escaping?
    Quote,
    // Only the [ mark
    OpenBrace,
    // Only the ] mark
    CloseBrace,
    // Karma (only complete chunk makes it in) (':++:', '++', '--', '+-', '±' for now)
    // TODO: this is eager, will need to figure out if we want to deal with "+++" at all
    Karma(&'a str)
}

impl <'a> fmt::Display for KarmaToken<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KarmaToken::Text(t)    => write!(f, "{}", t),
            KarmaToken::Space(t)   => write!(f, "{}", t),
            KarmaToken::Karma(t)   => write!(f, "{}", t),
            KarmaToken::Quote      => write!(f, "\""),
            KarmaToken::OpenBrace  => write!(f, "["),
            KarmaToken::CloseBrace => write!(f, "]"),
        }
    }
}

fn all_token(input: &str) -> IResult<&str, Vec<KarmaToken>> {
    many0(token)(input)
}

fn token(input: &str) -> IResult<&str, KarmaToken> {
    alt((
        map(tag(":++:"), |k| KarmaToken::Karma(k)),
        map(tag("++"),   |k| KarmaToken::Karma(k)),
        map(tag("--"),   |k| KarmaToken::Karma(k)),
        map(tag("+-"),   |k| KarmaToken::Karma(k)),
        map(tag("±"),    |k| KarmaToken::Karma(k)),
        map(tag("\""),   |_| KarmaToken::Quote),
        map(tag("["),    |_| KarmaToken::OpenBrace),
        map(tag("]"),    |_| KarmaToken::CloseBrace),
        space,
        text,
    ))(input)
}

fn space(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_whitespace()), |s| KarmaToken::Space(s))(input)
}

fn text(input: &str) -> IResult<&str, KarmaToken> {
    map(take_while1(|c:char| c.is_alphanumeric()), |s| KarmaToken::Text(s))(input)
}

// Engine for allowing us to parse on top of tokens
#[derive(Debug, PartialEq, Clone, Copy)]
struct Tokens<'a> {
    tok: &'a [KarmaToken<'a>],
    start: usize,
    end: usize,
}

impl<'a> Tokens<'a> {
    fn new(vec: &'a Vec<KarmaToken<'a>>) -> Self {
        Tokens {
            tok: vec.as_slice(),
            start: 0,
            end: vec.len(),
        }
    }
}

impl<'a> InputLength for Tokens<'a> {
    #[inline]
    fn input_len(&self) -> usize {
        self.tok.len()
    }
}

impl<'a> InputTake for Tokens<'a> {
    #[inline]
    fn take(&self, count: usize) -> Self {
        Tokens {
            tok: &self.tok[0..count],
            start: 0,
            end: count,
        }
    }

    #[inline]
    fn take_split(&self, count: usize) -> (Self, Self) {
        let (prefix, suffix) = self.tok.split_at(count);
        let first = Tokens {
            tok: prefix,
            start: 0,
            end: prefix.len(),
        };
        let second = Tokens {
            tok: suffix,
            start: 0,
            end: suffix.len(),
        };
        (second, first)
    }
}

impl<'a> InputLength for KarmaToken<'a> {
    #[inline]
    fn input_len(&self) -> usize {
        1
    }
}

impl<'a> Slice<Range<usize>> for Tokens<'a> {
    #[inline]
    fn slice(&self, range: Range<usize>) -> Self {
        Tokens {
            tok: self.tok.slice(range.clone()),
            start: self.start + range.start,
            end: self.start + range.end,
        }
    }
}

impl<'a> Slice<RangeTo<usize>> for Tokens<'a> {
    #[inline]
    fn slice(&self, range: RangeTo<usize>) -> Self {
        self.slice(0..range.end)
    }
}

impl<'a> Slice<RangeFrom<usize>> for Tokens<'a> {
    #[inline]
    fn slice(&self, range: RangeFrom<usize>) -> Self {
        self.slice(range.start..self.end - self.start)
    }
}

impl<'a> Slice<RangeFull> for Tokens<'a> {
    #[inline]
    fn slice(&self, _: RangeFull) -> Self {
        Tokens {
            tok: self.tok,
            start: self.start,
            end: self.end,
        }
    }
}

impl<'a> InputIter for Tokens<'a> {
    type Item = &'a KarmaToken<'a>;
    type Iter = Enumerate<::std::slice::Iter<'a, KarmaToken<'a>>>;
    type IterElem = ::std::slice::Iter<'a, KarmaToken<'a>>;

    #[inline]
    fn iter_indices(&self) -> Enumerate<::std::slice::Iter<'a, KarmaToken<'a>>> {
        self.tok.iter().enumerate()
    }
    #[inline]
    fn iter_elements(&self) -> ::std::slice::Iter<'a, KarmaToken<'a>> {
        self.tok.iter()
    }
    #[inline]
    fn position<P>(&self, predicate: P) -> Option<usize>
    where
        P: Fn(Self::Item) -> bool,
    {
        self.tok.iter().position(|b| predicate(b))
    }
    #[inline]
    fn slice_index(&self, count: usize) -> Result<usize, nom::Needed> {
        if self.tok.len() >= count {
            Ok(count)
        } else {
            Err(nom::Needed::Unknown)
        }
    }
}

impl UnspecializedInput for Tokens<'_> {}

// Now here begins the actual structural karma parser (Karma Structure Tree (KST))
// Some simple rules for now:
// 1. asdf
#[derive(Debug, PartialEq)]
enum KST {
    Test
}

fn structural_karma(input: Tokens) -> IResult<Tokens, KST> {
    map(
        take_while1(|c:&KarmaToken| {
            match c {
                KarmaToken::Quote   => true,
                _ => false,
            }
        }),
        |_| KST::Test
    )(input)
}

#[cfg(test)]
mod test_structural_karma {
    use super::*;

    #[test]
    fn test_karma() {
        let token = all_token("\"\"++");

        let kst = token.unwrap().1;
        let kst = structural_karma(Tokens::new(&kst));

        let empty = vec![KarmaToken::Karma("++")];
        let empty = Tokens::new(&empty);

        assert_eq!(kst, Ok((empty, KST::Test)));
    }
}

#[cfg(test)]
mod test_karma_token {
    use super::*;

    #[test]
    fn test_karma() {
        let karma = vec![":++:", "++", "--", "+-", "±"];

        assert_eq!(
            all_token(&karma.join("")),
            Ok(("", karma.iter().map(|k| KarmaToken::Karma(k)).collect()))
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(
            all_token("\"\"\""),
            Ok(("", vec![KarmaToken::Quote; 3]))
        );
    }

    #[test]
    fn test_brace() {
        assert_eq!(
            all_token("][]]"),
            Ok(("", vec![
                KarmaToken::CloseBrace,
                KarmaToken::OpenBrace,
                KarmaToken::CloseBrace,
                KarmaToken::CloseBrace
            ]))
        );
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(
            all_token(" \t"),
            Ok(("", vec![KarmaToken::Space(" \t")]))
        );
    }

    #[test]
    fn test_whitespace_block() {
        assert_eq!(
            all_token(" ]\t[  "),
            Ok(("", vec![
                KarmaToken::Space(" "),
                KarmaToken::CloseBrace,
                KarmaToken::Space("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Space("  ")
            ]))
        );
    }

    #[test]
    fn test_text() {
        assert_eq!(
            all_token("this藏test"),
            Ok(("", vec![KarmaToken::Text("this藏test")]))
        );
    }

    #[test]
    fn test_text_block() {
        assert_eq!(
            all_token("this\t[\"藏  ++]"),
            Ok(("", vec![
                KarmaToken::Text("this"),
                KarmaToken::Space("\t"),
                KarmaToken::OpenBrace,
                KarmaToken::Quote,
                KarmaToken::Text("藏"),
                KarmaToken::Space("  "),
                KarmaToken::Karma("++"),
                KarmaToken::CloseBrace,
            ]))
        );
    }

    #[test]
    fn test_roundtrip() {
        let text = "this\t[\"藏  ++]";
        let parse = all_token(text).unwrap().1;

        assert_eq!(parse.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(""), text);
    }
}

#[cfg(test)]
mod test_command {
    use super::*;

    #[test]
    fn test_command() {
        assert_eq!(
            command("!karma"),
            Ok(("", Command("karma", vec![])))
        );
    }

    #[test]
    fn test_command_one_arg() {
        assert_eq!(
            command("!karma a"),
            Ok(("", Command("karma", vec!["a"])))
        );
    }

    #[test]
    fn test_command_two_arg() {
        assert_eq!(
            command("!karma a bcd"),
            Ok(("", Command("karma", vec!["a", "bcd"])))
        );
    }

    #[test]
    fn test_command_one_quote_arg() {
        assert_eq!(
            command("!karma \"a b\""),
            Ok(("", Command("karma", vec!["a b"])))
        );
    }

    #[test]
    fn test_command_two_quote_arg() {
        assert_eq!(
            command("!karma \"a b\" \"c d\""),
            Ok(("", Command("karma", vec!["a b", "c d"])))
        );
    }

    #[test]
    fn test_command_two_quote_and_unquote_arg() {
        assert_eq!(
            command("!karma \"a b\" cd"),
            Ok(("", Command("karma", vec!["a b", "cd"])))
        );
    }

    #[test]
    fn test_command_fail_close_arg() {
        assert_eq!(
            command("!karma \"a"),
            Ok(("", Command("karma", vec!["\"a"])))
        );
    }

    #[test]
    fn test_command_fail_open_arg() {
        assert_eq!(
            command("!karma a\""),
            Ok(("", Command("karma", vec!["a\""])))
        );
    }

    // TODO: decide how to deal with this case
//    #[test]
//    fn test_command_multi_quote_arg() {
//        assert_eq!(
//            command("!karma \"\"a\"\""),
//            Ok(("", Command("karma", vec!["a"])))
//        );
//    }

    // TODO: decide how to deal with this case
//    #[test]
//    fn test_command_escape_quote_arg() {
//        assert_eq!(
//            command("!karma \"\\\"a\\\"\""),
//            Ok(("", Command("karma", vec!["\"a\""])))
//        );
//    }
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate metadeps;

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Set up compiler options.
    let mut cflags = env::var("CFLAGS").unwrap_or_else(|_| String::new());
    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let _ = fs::create_dir(&build);

    let mut config_opts = Vec::new();
    config_opts.push("--enable-static=yes".to_string());
    config_opts.push("--enable-shared=no".to_string());
    config_opts.push("--enable-optimize".to_string());
    config_opts.push(format!("--prefix={}", dst.display()));

    // Prepare.
    let src = env::current_dir().unwrap();
    run(
        Command::new("sh")
            .current_dir(&src.join("c-ares"))
            .arg("buildconf"),
    );
    run(
        Command::new("sh")
            .env("CFLAGS", &cflags)
            .current_dir(&build)
            .arg("-c")
            .arg(format!(
                "{} {}",
                src.join("c-ares/configure").display(),
                config_opts.join(" ")
            )),
    );

    // Compile.
    run(
        Command::new(make())
            .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
            .current_dir(&build),
    );

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = gcc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.
    let src = env::current_dir().unwrap();
    let c_ares_dir = &src.join("c-ares");
    run(
        Command::new("cmd")
            .current_dir(c_ares_dir)
            .arg("/c")
            .arg("buildconf.bat"),
    );

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<commit_msg>only rebuild c-ares when needed<commit_after>extern crate gcc;
extern crate metadeps;

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // Rerun if the c-ares source code has changed.
    println!("cargo:rerun-if-changed=c-ares");

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Set up compiler options.
    let mut cflags = env::var("CFLAGS").unwrap_or_else(|_| String::new());
    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let _ = fs::create_dir(&build);

    let mut config_opts = Vec::new();
    config_opts.push("--enable-static=yes".to_string());
    config_opts.push("--enable-shared=no".to_string());
    config_opts.push("--enable-optimize".to_string());
    config_opts.push(format!("--prefix={}", dst.display()));

    // Prepare.
    let src = env::current_dir().unwrap();
    run(
        Command::new("sh")
            .current_dir(&src.join("c-ares"))
            .arg("buildconf"),
    );
    run(
        Command::new("sh")
            .env("CFLAGS", &cflags)
            .current_dir(&build)
            .arg("-c")
            .arg(format!(
                "{} {}",
                src.join("c-ares/configure").display(),
                config_opts.join(" ")
            )),
    );

    // Compile.
    run(
        Command::new(make())
            .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
            .current_dir(&build),
    );

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = gcc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.
    let src = env::current_dir().unwrap();
    let c_ares_dir = &src.join("c-ares");
    run(
        Command::new("cmd")
            .current_dir(c_ares_dir)
            .arg("/c")
            .arg("buildconf.bat"),
    );

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
use {GameDataRead, GameDataWrite, Texture, Textures};
use gamedata_io::{Chunk, get_chunk_header, ReadError, read_into_byte_vec, Tell};

const IMAGE_DATA_ALIGNMENT: u32 = 128;

impl<'a> Chunk<'a> for Textures {
    const TYPE_ID: &'static [u8; 4] = b"TXTR";
    type ReadOutput = Self;
    fn read<R: GameDataRead>(reader: &mut R) -> Result<Textures, ReadError> {
        try!(get_chunk_header(reader, Self::TYPE_ID));
        let num_textures = try!(reader.read_u32::<LittleEndian>());
        trace!("{} textures", num_textures);
        // Read texture entry offsets
        for _ in 0..num_textures {
            // For now just discard them
            try!(reader.read_u32::<LittleEndian>());
        }
        let mut textures = Vec::new();
        let mut finished_offset = 0;
        for i in 0..num_textures {
            let unk = try!(reader.read_u32::<LittleEndian>());
            let offset = try!(reader.read_u32::<LittleEndian>());
            let reader_offset = try!(reader.tell());
            try!(reader.seek(io::SeekFrom::Start(offset as u64)));
            assert!(offset % IMAGE_DATA_ALIGNMENT == 0,
                    "Image data is assumed to be aligned on {} byte boundaries", IMAGE_DATA_ALIGNMENT);
            trace!("Reading image data {} @ {}", i, offset);
            let png = try!(read_png(reader));
            finished_offset = try!(reader.tell());
            try!(reader.seek(io::SeekFrom::Start(reader_offset)));
            textures.push(Texture {
                unknown: unk,
                png_data: png,
            });
        }
        // Looks like chunks don't use the same alignment as image data.
        // Or maybe they don't use alignment at all?
        // Why the zero padding then?
        while finished_offset % 16 != 0 {
            finished_offset += 1;
        }
        try!(reader.seek(io::SeekFrom::Start(finished_offset)));
        Ok(Textures {
            textures: textures,
        })
    }
    chunk_write_impl!();
    fn write_content<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        try!(writer.write_u32::<LittleEndian>(self.textures.len() as u32));
        let start_offset = try!(writer.tell());
        let num_textures = self.textures.len() as u32;
        let offset_table_len = num_textures * 4;
        let fileinfo_table_len = num_textures * 8;
        let mut texture_data_offsets: Vec<u32> = Vec::with_capacity(num_textures as usize);
        let offset_table_offset = try!(writer.tell());
        // Skip offset table and fileinfos and write image data first
        try!(writer.seek(io::SeekFrom::Current((offset_table_len + fileinfo_table_len) as i64)));
        for (i, t) in self.textures.iter().enumerate() {
            let mut offset = try!(writer.tell());
            while offset % IMAGE_DATA_ALIGNMENT as u64 != 0 {
                offset += 1;
            }
            try!(writer.seek(io::SeekFrom::Start(offset)));
            trace!("Writing image data {} @ {}", i, offset);
            try!(writer.write_all(&t.png_data));
            texture_data_offsets.push(offset as u32);
        }
        let finished_offset = try!(writer.tell());
        // Go back and write offset table
        try!(writer.seek(io::SeekFrom::Start(offset_table_offset)));
        for i in 0..num_textures {
            try!(writer.write_u32::<LittleEndian>(start_offset as u32 + offset_table_len +
                                                  (i * 8)));
        }
        // Write fileinfos
        for (t, &off) in self.textures.iter().zip(texture_data_offsets.iter()) {
            try!(writer.write_u32::<LittleEndian>(t.unknown));
            try!(writer.write_u32::<LittleEndian>(off));
        }
        try!(writer.seek(io::SeekFrom::Start(finished_offset)));
        trace!("Finished at {}", finished_offset);
        let mut offset = finished_offset;
        // Write zero padding
        while offset % 16 != 0 {
            offset += 1;
            try!(writer.write_u8(0));
        }
        Ok(())
    }
}

fn png_length<R: GameDataRead>(reader: &mut R) -> Result<u32, ReadError> {
    const MAGIC: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
    let reader_start = try!(reader.seek(io::SeekFrom::Current(0)));
    let mut buf = [0u8; 8];
    try!(reader.read_exact(&mut buf));
    assert_eq!(buf, MAGIC);
    loop {
        let length = try!(reader.read_u32::<BigEndian>());
        let mut chunk_type = [0u8; 4];
        try!(reader.read_exact(&mut chunk_type));
        let crc_len = 4;
        try!(reader.seek(io::SeekFrom::Current(length as i64 + crc_len)));
        if &chunk_type == b"IEND" {
            break;
        }
    }
    let reader_end = try!(reader.seek(io::SeekFrom::Current(0)));
    let length = reader_end - reader_start;
    try!(reader.seek(io::SeekFrom::Start(reader_start)));
    trace!("Length of PNG is {}", length);
    Ok(length as u32)
}

fn read_png<R: GameDataRead>(reader: &mut R) -> Result<Vec<u8>, ReadError> {
    let length = try!(png_length(reader));
    let buf = try!(read_into_byte_vec(reader, length as usize));
    Ok(buf)
}
<commit_msg>Format code with rustfmt<commit_after>use std::io::prelude::*;
use std::io;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
use {GameDataRead, GameDataWrite, Texture, Textures};
use gamedata_io::{Chunk, get_chunk_header, ReadError, read_into_byte_vec, Tell};

const IMAGE_DATA_ALIGNMENT: u32 = 128;

impl<'a> Chunk<'a> for Textures {
    const TYPE_ID: &'static [u8; 4] = b"TXTR";
    type ReadOutput = Self;
    fn read<R: GameDataRead>(reader: &mut R) -> Result<Textures, ReadError> {
        try!(get_chunk_header(reader, Self::TYPE_ID));
        let num_textures = try!(reader.read_u32::<LittleEndian>());
        trace!("{} textures", num_textures);
        // Read texture entry offsets
        for _ in 0..num_textures {
            // For now just discard them
            try!(reader.read_u32::<LittleEndian>());
        }
        let mut textures = Vec::new();
        let mut finished_offset = 0;
        for i in 0..num_textures {
            let unk = try!(reader.read_u32::<LittleEndian>());
            let offset = try!(reader.read_u32::<LittleEndian>());
            let reader_offset = try!(reader.tell());
            try!(reader.seek(io::SeekFrom::Start(offset as u64)));
            assert!(offset % IMAGE_DATA_ALIGNMENT == 0,
                    "Image data is assumed to be aligned on {} byte boundaries",
                    IMAGE_DATA_ALIGNMENT);
            trace!("Reading image data {} @ {}", i, offset);
            let png = try!(read_png(reader));
            finished_offset = try!(reader.tell());
            try!(reader.seek(io::SeekFrom::Start(reader_offset)));
            textures.push(Texture {
                unknown: unk,
                png_data: png,
            });
        }
        // Looks like chunks don't use the same alignment as image data.
        // Or maybe they don't use alignment at all?
        // Why the zero padding then?
        while finished_offset % 16 != 0 {
            finished_offset += 1;
        }
        try!(reader.seek(io::SeekFrom::Start(finished_offset)));
        Ok(Textures { textures: textures })
    }
    chunk_write_impl!();
    fn write_content<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        try!(writer.write_u32::<LittleEndian>(self.textures.len() as u32));
        let start_offset = try!(writer.tell());
        let num_textures = self.textures.len() as u32;
        let offset_table_len = num_textures * 4;
        let fileinfo_table_len = num_textures * 8;
        let mut texture_data_offsets: Vec<u32> = Vec::with_capacity(num_textures as usize);
        let offset_table_offset = try!(writer.tell());
        // Skip offset table and fileinfos and write image data first
        try!(writer.seek(io::SeekFrom::Current((offset_table_len + fileinfo_table_len) as i64)));
        for (i, t) in self.textures.iter().enumerate() {
            let mut offset = try!(writer.tell());
            while offset % IMAGE_DATA_ALIGNMENT as u64 != 0 {
                offset += 1;
            }
            try!(writer.seek(io::SeekFrom::Start(offset)));
            trace!("Writing image data {} @ {}", i, offset);
            try!(writer.write_all(&t.png_data));
            texture_data_offsets.push(offset as u32);
        }
        let finished_offset = try!(writer.tell());
        // Go back and write offset table
        try!(writer.seek(io::SeekFrom::Start(offset_table_offset)));
        for i in 0..num_textures {
            try!(writer.write_u32::<LittleEndian>(start_offset as u32 + offset_table_len +
                                                  (i * 8)));
        }
        // Write fileinfos
        for (t, &off) in self.textures.iter().zip(texture_data_offsets.iter()) {
            try!(writer.write_u32::<LittleEndian>(t.unknown));
            try!(writer.write_u32::<LittleEndian>(off));
        }
        try!(writer.seek(io::SeekFrom::Start(finished_offset)));
        trace!("Finished at {}", finished_offset);
        let mut offset = finished_offset;
        // Write zero padding
        while offset % 16 != 0 {
            offset += 1;
            try!(writer.write_u8(0));
        }
        Ok(())
    }
}

fn png_length<R: GameDataRead>(reader: &mut R) -> Result<u32, ReadError> {
    const MAGIC: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
    let reader_start = try!(reader.seek(io::SeekFrom::Current(0)));
    let mut buf = [0u8; 8];
    try!(reader.read_exact(&mut buf));
    assert_eq!(buf, MAGIC);
    loop {
        let length = try!(reader.read_u32::<BigEndian>());
        let mut chunk_type = [0u8; 4];
        try!(reader.read_exact(&mut chunk_type));
        let crc_len = 4;
        try!(reader.seek(io::SeekFrom::Current(length as i64 + crc_len)));
        if &chunk_type == b"IEND" {
            break;
        }
    }
    let reader_end = try!(reader.seek(io::SeekFrom::Current(0)));
    let length = reader_end - reader_start;
    try!(reader.seek(io::SeekFrom::Start(reader_start)));
    trace!("Length of PNG is {}", length);
    Ok(length as u32)
}

fn read_png<R: GameDataRead>(reader: &mut R) -> Result<Vec<u8>, ReadError> {
    let length = try!(png_length(reader));
    let buf = try!(read_into_byte_vec(reader, length as usize));
    Ok(buf)
}
<|endoftext|>