<commit_before>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        print_header(&self.rustbox,
                     self.rustbox.width(),
                     &format!("{} - {} [{}/{}]", item.title, title, item.page, item.max_page));
        print_body(&self.rustbox,
                   self.body_width(),
                   2,
                   self.body_height(),
                   &item,
                   self.scrollY);
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let title_len = jks_len(text);
    let padding = (if width >= title_len {
        width - title_len
    } else {
        0
    }) / 2;

    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

// fn display(vec: Vec<NodeType>, depth: u8) {
//     for (index, node) in vec.iter().enumerate() {
//         let node2 = node.clone();
//         let spaces = (0..depth).map(|_| ">").collect::<Vec<_>>().join("");
//         match node2 {
//             NodeType::Text(n) => {
//                 println!("{}[{}] = Text {}", spaces, index, n.data);
//             }
//             NodeType::Image(n) => {
//                 println!("{}[{}] = Image {}", spaces, index, n.data);
//             }
//             NodeType::BlockQuote(n) => {
//                 println!("{}[{}] = BlockQuote", spaces, index);
//                 display(n.data, depth + 1);
//             }
//             NodeType::Br(n) => {
//                 println!("{}[{}] = Br", spaces, index);
//             }
//         }
//     }
// }

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String){
    rustbox.print(0,
                  y,
                  rustbox::RB_NORMAL,
                  Color::White,
                  Color::Black,
                  &s);
}

fn print_reply(vec: &Vec<NodeType>, depth: u8, scrollY: usize, y: usize, rustbox: &rustbox::RustBox) -> usize {
    let mut m = 0; let mut o = 0;
    for (j, node) in vec.iter().enumerate() {
        if scrollY + 1 < y + m + o {

            let node2 = node.clone();
            let spaces = (0..depth).map(|_| ">").collect::<Vec<_>>().join("");
            match node2 {
                NodeType::Text(n) => { print_default(rustbox, 0, j + y + o - scrollY, format!("{}[{}] = Text {}", spaces, j, n.data)) }
                NodeType::Image(n) => { print_default(rustbox, 0, j + y + o - scrollY, format!("{}[{}] = Image {}", spaces, j, n.data)) }
                NodeType::BlockQuote(n) => { print_default(rustbox, 0, j + y + o - scrollY, format!("{}[{}] = BlockQuote", spaces, j)); o+= print_reply(&n.data, depth + 1, scrollY, y + m + o + 1, &rustbox); }
                NodeType::Br(n) => { print_default(rustbox, 0, j + y + o - scrollY, format!("{}[{}] = Br", spaces, j)) }
            }
        }

        m +=1;
    }
    m + o
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              item: &ShowItem,
              scrollY: usize) {

    let mut y = offset_y;
    let replier_max_width = 14;
    let time_max_width = 5;
    let now = Local::now();

    let separator_width = if rustbox.width() >= 2 {
        rustbox.width() - 2
    } else {
        0
    };
    let separator_padding_width = if rustbox.width() > separator_width {
        rustbox.width() - separator_width
    } else {
        0
    } / 2;

    let separator_padding = (0..separator_padding_width).map(|_| " ").collect::<Vec<_>>().join("");

    let separator_bottom = make_separator_bottom(separator_width, &separator_padding);

    for (i, reply) in item.replies.iter().take(rows).enumerate() {

        let mut m = print_reply(&reply.body, 0, scrollY, y, &rustbox);

        if scrollY + 1 < y + m {

            let replier_name = reply.username.clone();

            let published_at = reply.published_at.clone();

            let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
                Ok(v) => v,
                Err(e) => now,
            };
            let time = published_at_format(&(now - published_at_dt));

            let separator_top = make_separator_top(separator_width,
                                                   &separator_padding,
                                                   replier_max_width,
                                                   &replier_name,
                                                   time_max_width,
                                                   &time);

            rustbox.print(0,
                          m + y - scrollY,
                          rustbox::RB_NORMAL,
                          Color::Green,
                          Color::Black,
                          &separator_top);
        }
        m += 1;

        if scrollY + 1 < y + m {
            rustbox.print(0,
                          m + y - scrollY,
                          rustbox::RB_NORMAL,
                          Color::Green,
                          Color::Black,
                          &separator_bottom);
        }
        m += 1;
        y += m;
    }
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = (0..replier_name_left_spacing_width)
                                        .map(|_| "─")
                                        .collect::<Vec<_>>()
                                        .join("");

    let replier_name_right_spacing = (0..replier_name_right_spacing_width)
                                         .map(|_| "─")
                                         .collect::<Vec<_>>()
                                         .join("");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = (0..time_left_spacing_width)
                                .map(|_| "─")
                                .collect::<Vec<_>>()
                                .join("");

    let time_right_spacing = (0..time_right_spacing_width)
                                 .map(|_| "─")
                                 .collect::<Vec<_>>()
                                 .join("");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = (0..separator_top_middle_width)
                                   .map(|_| " ")
                                   .collect::<Vec<_>>()
                                   .join("");

    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = (0..separator_bottom_middle_width)
                                      .map(|_| "─")
                                      .collect::<Vec<_>>()
                                      .join("");
    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}
<commit_msg>show - refactor print_reply - more meaningful Y + simplify Y<commit_after>extern crate rustbox;
extern crate chrono;

use rustbox::*;
use chrono::*;

use screen::common::*;
use utility::string::*;
use model::ShowItem;
use reply_model::*;

pub struct Show<'a> {
    rustbox: &'a rustbox::RustBox,
    scrollY: usize,
}

impl<'a> Show<'a> {
    pub fn new(rustbox: &'a rustbox::RustBox) -> Self {
        Show {
            rustbox: &rustbox,
            scrollY: 0,
        }
    }
    pub fn print(&mut self, title: &str, item: &ShowItem) {

        print_header(&self.rustbox,
                     self.rustbox.width(),
                     &format!("{} - {} [{}/{}]", item.title, title, item.page, item.max_page));
        print_body(&self.rustbox,
                   self.body_width(),
                   2,
                   self.body_height(),
                   &item,
                   self.scrollY);
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp < 10000 {
            self.scrollY = tmp + value;
            return true;
        }
        false
    }

    pub fn body_height(&self) -> usize {
        if self.rustbox.height() >= 3 {
            self.rustbox.height() - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {
        if self.rustbox.width() >= 2 {
            self.rustbox.width() - 2
        } else {
            0
        }
    }
}

fn print_header(rustbox: &rustbox::RustBox, width: usize, text: &str) {
    let title_len = jks_len(text);
    let padding = (if width >= title_len {
        width - title_len
    } else {
        0
    }) / 2;

    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    clearline(&rustbox, width, 0, 0);
    rustbox.print(padding,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  text);
    rustbox.print(0,
                  1,
                  rustbox::RB_BOLD,
                  Color::Yellow,
                  Color::Black,
                  &header_bottom);
}

// fn display(vec: Vec<NodeType>, depth: u8) {
//     for (index, node) in vec.iter().enumerate() {
//         let node2 = node.clone();
//         let spaces = (0..depth).map(|_| ">").collect::<Vec<_>>().join("");
//         match node2 {
//             NodeType::Text(n) => {
//                 println!("{}[{}] = Text {}", spaces, index, n.data);
//             }
//             NodeType::Image(n) => {
//                 println!("{}[{}] = Image {}", spaces, index, n.data);
//             }
//             NodeType::BlockQuote(n) => {
//                 println!("{}[{}] = BlockQuote", spaces, index);
//                 display(n.data, depth + 1);
//             }
//             NodeType::Br(n) => {
//                 println!("{}[{}] = Br", spaces, index);
//             }
//         }
//     }
// }

fn print_default(rustbox: &rustbox::RustBox, x: usize, y: usize, s: String){
    rustbox.print(0,
                  y,
                  rustbox::RB_NORMAL,
                  Color::White,
                  Color::Black,
                  &s);
}

fn print_reply(vec: &Vec<NodeType>, depth: u8, scrollY: usize, y: usize, rustbox: &rustbox::RustBox) -> usize {
    let mut recursive_offset = 0;

    for (j, node) in vec.iter().enumerate() {
        let total_y = y + j + recursive_offset;
        if scrollY + 1 < total_y {
            let node2 = node.clone();
            let padding = (0..depth).map(|_| ">").collect::<Vec<_>>().join("");
            match node2 {
                NodeType::Text(n) => { print_default(rustbox, 0, total_y - scrollY, format!("{}[{}] = Text {}", padding, j, n.data)) }
                NodeType::Image(n) => { print_default(rustbox, 0, total_y - scrollY, format!("{}[{}] = Image {}", padding, j, n.data)) }
                NodeType::BlockQuote(n) => { print_default(rustbox, 0, total_y - scrollY, format!("{}[{}] = BlockQuote", padding, j)); recursive_offset += print_reply(&n.data, depth + 1, scrollY, total_y + 1, &rustbox); }
                NodeType::Br(n) => { print_default(rustbox, 0, total_y - scrollY, format!("{}[{}] = Br", padding, j)) }
            }
        }
    }
    vec.len() + recursive_offset
}

fn print_body(rustbox: &rustbox::RustBox,
              width: usize,
              offset_y: usize,
              rows: usize,
              item: &ShowItem,
              scrollY: usize) {

    let mut y = offset_y;
    let replier_max_width = 14;
    let time_max_width = 5;
    let now = Local::now();

    let separator_width = if rustbox.width() >= 2 {
        rustbox.width() - 2
    } else {
        0
    };
    let separator_padding_width = if rustbox.width() > separator_width {
        rustbox.width() - separator_width
    } else {
        0
    } / 2;

    let separator_padding = (0..separator_padding_width).map(|_| " ").collect::<Vec<_>>().join("");

    let separator_bottom = make_separator_bottom(separator_width, &separator_padding);

    for (i, reply) in item.replies.iter().take(rows).enumerate() {

        let mut m = print_reply(&reply.body, 0, scrollY, y, &rustbox);

        if scrollY + 1 < y + m {

            let replier_name = reply.username.clone();

            let published_at = reply.published_at.clone();

            let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
                Ok(v) => v,
                Err(e) => now,
            };
            let time = published_at_format(&(now - published_at_dt));

            let separator_top = make_separator_top(separator_width,
                                                   &separator_padding,
                                                   replier_max_width,
                                                   &replier_name,
                                                   time_max_width,
                                                   &time);

            rustbox.print(0,
                          m + y - scrollY,
                          rustbox::RB_NORMAL,
                          Color::Green,
                          Color::Black,
                          &separator_top);
        }
        m += 1;

        if scrollY + 1 < y + m {
            rustbox.print(0,
                          m + y - scrollY,
                          rustbox::RB_NORMAL,
                          Color::Green,
                          Color::Black,
                          &separator_bottom);
        }
        m += 1;
        y += m;
    }
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = (0..replier_name_left_spacing_width)
                                        .map(|_| "─")
                                        .collect::<Vec<_>>()
                                        .join("");

    let replier_name_right_spacing = (0..replier_name_right_spacing_width)
                                         .map(|_| "─")
                                         .collect::<Vec<_>>()
                                         .join("");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = (0..time_left_spacing_width)
                                .map(|_| "─")
                                .collect::<Vec<_>>()
                                .join("");

    let time_right_spacing = (0..time_right_spacing_width)
                                 .map(|_| "─")
                                 .collect::<Vec<_>>()
                                 .join("");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = (0..separator_top_middle_width)
                                   .map(|_| " ")
                                   .collect::<Vec<_>>()
                                   .join("");

    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = (0..separator_bottom_middle_width)
                                      .map(|_| "─")
                                      .collect::<Vec<_>>()
                                      .join("");
    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-windows - this is a unix-specific test

#![feature(process_exec, libc)]

extern crate libc;

use std::env;
use std::io::Error;
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

fn main() {
    if let Some(arg) = env::args().skip(1).next() {
        match &arg[..] {
            "test1" => println!("hello2"),
            "test2" => assert_eq!(env::var("FOO").unwrap(), "BAR"),
            "test3" => assert_eq!(env::current_dir().unwrap()
                                      .to_str().unwrap(), "/"),
            "empty" => {}
            _ => panic!("unknown argument: {}", arg),
        }
        return
    }

    let me = env::current_exe().unwrap();

    let output = Command::new(&me).arg("test1").before_exec(|| {
        println!("hello");
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert_eq!(output.stdout, b"hello\nhello2\n");

    let output = Command::new(&me).arg("test2").before_exec(|| {
        env::set_var("FOO", "BAR");
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let output = Command::new(&me).arg("test3").before_exec(|| {
        env::set_current_dir("/").unwrap();
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let output = Command::new(&me).arg("bad").before_exec(|| {
        Err(Error::from_raw_os_error(102))
    }).output().err().unwrap();
    assert_eq!(output.raw_os_error(), Some(102));

    let pid = unsafe { libc::getpid() };
    assert!(pid >= 0);
    let output = Command::new(&me).arg("empty").before_exec(move || {
        let child = unsafe { libc::getpid() };
        assert!(child >= 0);
        assert!(pid != child);
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let mem = Arc::new(AtomicUsize::new(0));
    let mem2 = mem.clone();
    let output = Command::new(&me).arg("empty").before_exec(move || {
        assert_eq!(mem2.fetch_add(1, Ordering::SeqCst), 0);
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());
    assert_eq!(mem.load(Ordering::SeqCst), 0);
}
<commit_msg>Auto merge of #32193 - eddyb:stage1-is-useful-mkay, r=eddyb<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-windows - this is a unix-specific test
// no-prefer-dynamic - this test breaks with dynamic linking as
// some LD_LIBRARY_PATH entries are relative and it cd's to /.

#![feature(process_exec, libc)]

extern crate libc;

use std::env;
use std::io::Error;
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

fn main() {
    if let Some(arg) = env::args().skip(1).next() {
        match &arg[..] {
            "test1" => println!("hello2"),
            "test2" => assert_eq!(env::var("FOO").unwrap(), "BAR"),
            "test3" => assert_eq!(env::current_dir().unwrap()
                                      .to_str().unwrap(), "/"),
            "empty" => {}
            _ => panic!("unknown argument: {}", arg),
        }
        return
    }

    let me = env::current_exe().unwrap();

    let output = Command::new(&me).arg("test1").before_exec(|| {
        println!("hello");
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert_eq!(output.stdout, b"hello\nhello2\n");

    let output = Command::new(&me).arg("test2").before_exec(|| {
        env::set_var("FOO", "BAR");
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let output = Command::new(&me).arg("test3").before_exec(|| {
        env::set_current_dir("/").unwrap();
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let output = Command::new(&me).arg("bad").before_exec(|| {
        Err(Error::from_raw_os_error(102))
    }).output().err().unwrap();
    assert_eq!(output.raw_os_error(), Some(102));

    let pid = unsafe { libc::getpid() };
    assert!(pid >= 0);
    let output = Command::new(&me).arg("empty").before_exec(move || {
        let child = unsafe { libc::getpid() };
        assert!(child >= 0);
        assert!(pid != child);
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());

    let mem = Arc::new(AtomicUsize::new(0));
    let mem2 = mem.clone();
    let output = Command::new(&me).arg("empty").before_exec(move || {
        assert_eq!(mem2.fetch_add(1, Ordering::SeqCst), 0);
        Ok(())
    }).output().unwrap();
    assert!(output.status.success());
    assert!(output.stderr.is_empty());
    assert!(output.stdout.is_empty());
    assert_eq!(mem.load(Ordering::SeqCst), 0);
}
<|endoftext|>


<commit_before>//! This is a small client program intended to pair with `remote-test-server` in
//! this repository. This client connects to the server over TCP and is used to
//! push artifacts and run tests on the server instead of locally.
//!
//! Here is also where we bake in the support to spawn the QEMU emulator as
//! well.

#![deny(warnings)]

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::TcpStream;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;

const REMOTE_ADDR_ENV: &str = "TEST_DEVICE_ADDR";

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

fn main() {
    let mut args = env::args().skip(1);

    match &args.next().unwrap()[..] {
        "spawn-emulator" => spawn_emulator(
            &args.next().unwrap(),
            Path::new(&args.next().unwrap()),
            Path::new(&args.next().unwrap()),
            args.next().map(|s| s.into()),
        ),
        "push" => push(Path::new(&args.next().unwrap())),
        "run" => run(args.next().unwrap(), args.collect()),
        cmd => panic!("unknown command: {}", cmd),
    }
}

fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option<PathBuf>) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());

    if env::var(REMOTE_ADDR_ENV).is_ok() {
        println!("Connecting to remote device {} ...", device_address);
    } else if target.contains("android") {
        start_android_emulator(server);
    } else {
        let rootfs = rootfs.as_ref().expect("need rootfs on non-android");
        start_qemu_emulator(target, rootfs, server, tmpdir);
    }

    // Wait for the emulator to come online
    loop {
        let dur = Duration::from_millis(100);
        if let Ok(mut client) = TcpStream::connect(&device_address) {
            t!(client.set_read_timeout(Some(dur)));
            t!(client.set_write_timeout(Some(dur)));
            if client.write_all(b"ping").is_ok() {
                let mut b = [0; 4];
                if client.read_exact(&mut b).is_ok() {
                    break;
                }
            }
        }
        thread::sleep(dur);
    }
}

fn start_android_emulator(server: &Path) {
    println!("waiting for device to come online");
    let status = Command::new("adb").arg("wait-for-device").status().unwrap();
    assert!(status.success());

    println!("pushing server");
    let status =
        Command::new("adb").arg("push").arg(server).arg("/data/tmp/testd").status().unwrap();
    assert!(status.success());

    println!("forwarding tcp");
    let status =
        Command::new("adb").arg("forward").arg("tcp:12345").arg("tcp:12345").status().unwrap();
    assert!(status.success());

    println!("executing server");
    Command::new("adb").arg("shell").arg("/data/tmp/testd").spawn().unwrap();
}

fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path) {
    // Generate a new rootfs image now that we've updated the test server
    // executable. This is the equivalent of:
    //
    //      find $rootfs -print 0 | cpio --null -o --format=newc > rootfs.img
    t!(fs::copy(server, rootfs.join("testd")));
    let rootfs_img = tmpdir.join("rootfs.img");
    let mut cmd = Command::new("cpio");
    cmd.arg("--null")
        .arg("-o")
        .arg("--format=newc")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(rootfs);
    let mut child = t!(cmd.spawn());
    let mut stdin = child.stdin.take().unwrap();
    let rootfs = rootfs.to_path_buf();
    thread::spawn(move || add_files(&mut stdin, &rootfs, &rootfs));
    t!(io::copy(&mut child.stdout.take().unwrap(), &mut t!(File::create(&rootfs_img))));
    assert!(t!(child.wait()).success());

    // Start up the emulator, in the background
    match target {
        "arm-unknown-linux-gnueabihf" => {
            let mut cmd = Command::new("qemu-system-arm");
            cmd.arg("-M")
                .arg("vexpress-a15")
                .arg("-m")
                .arg("1024")
                .arg("-kernel")
                .arg("/tmp/zImage")
                .arg("-initrd")
                .arg(&rootfs_img)
                .arg("-dtb")
                .arg("/tmp/vexpress-v2p-ca15-tc1.dtb")
                .arg("-append")
                .arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
                .arg("-nographic")
                .arg("-redir")
                .arg("tcp:12345::12345");
            t!(cmd.spawn());
        }
        "aarch64-unknown-linux-gnu" => {
            let mut cmd = Command::new("qemu-system-aarch64");
            cmd.arg("-machine")
                .arg("virt")
                .arg("-cpu")
                .arg("cortex-a57")
                .arg("-m")
                .arg("1024")
                .arg("-kernel")
                .arg("/tmp/Image")
                .arg("-initrd")
                .arg(&rootfs_img)
                .arg("-append")
                .arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
                .arg("-nographic")
                .arg("-netdev")
                .arg("user,id=net0,hostfwd=tcp::12345-:12345")
                .arg("-device")
                .arg("virtio-net-device,netdev=net0,mac=00:00:00:00:00:00");
            t!(cmd.spawn());
        }
        _ => panic!("cannot start emulator for: {}" < target),
    }

    fn add_files(w: &mut dyn Write, root: &Path, cur: &Path) {
        for entry in t!(cur.read_dir()) {
            let entry = t!(entry);
            let path = entry.path();
            let to_print = path.strip_prefix(root).unwrap();
            t!(write!(w, "{}\u{0}", to_print.to_str().unwrap()));
            if t!(entry.file_type()).is_dir() {
                add_files(w, root, &path);
            }
        }
    }
}

fn push(path: &Path) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
    let client = t!(TcpStream::connect(device_address));
    let mut client = BufWriter::new(client);
    t!(client.write_all(b"push"));
    send(path, &mut client);
    t!(client.flush());

    // Wait for an acknowledgement that all the data was received. No idea
    // why this is necessary, seems like it shouldn't be!
    let mut client = client.into_inner().unwrap();
    let mut buf = [0; 4];
    t!(client.read_exact(&mut buf));
    assert_eq!(&buf, b"ack ");
    println!("done pushing {:?}", path);
}

fn run(files: String, args: Vec<String>) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
    let client = t!(TcpStream::connect(device_address));
    let mut client = BufWriter::new(client);
    t!(client.write_all(b"run "));

    // Send over the args
    for arg in args {
        t!(client.write_all(arg.as_bytes()));
        t!(client.write_all(&[0]));
    }
    t!(client.write_all(&[0]));

    // Send over env vars
    //
    // Don't send over *everything* though as some env vars are set by and used
    // by the client.
    for (k, v) in env::vars() {
        match &k[..] {
            "PATH" | "LD_LIBRARY_PATH" | "PWD" => continue,
            _ => {}
        }
        t!(client.write_all(k.as_bytes()));
        t!(client.write_all(&[0]));
        t!(client.write_all(v.as_bytes()));
        t!(client.write_all(&[0]));
    }
    t!(client.write_all(&[0]));

    // Send over support libraries
    let mut files = files.split(':');
    let exe = files.next().unwrap();
    for file in files.map(Path::new) {
        send(&file, &mut client);
    }
    t!(client.write_all(&[0]));

    // Send over the client executable as the last piece
    send(exe.as_ref(), &mut client);

    println!("uploaded {:?}, waiting for result", exe);

    // Ok now it's time to read all the output. We're receiving "frames"
    // representing stdout/stderr, so we decode all that here.
    let mut header = [0; 5];
    let mut stderr_done = false;
    let mut stdout_done = false;
    let mut client = t!(client.into_inner());
    let mut stdout = io::stdout();
    let mut stderr = io::stderr();
    while !stdout_done || !stderr_done {
        t!(client.read_exact(&mut header));
        let amt = ((header[1] as u64) << 24)
            | ((header[2] as u64) << 16)
            | ((header[3] as u64) << 8)
            | ((header[4] as u64) << 0);
        if header[0] == 0 {
            if amt == 0 {
                stdout_done = true;
            } else {
                t!(io::copy(&mut (&mut client).take(amt), &mut stdout));
                t!(stdout.flush());
            }
        } else {
            if amt == 0 {
                stderr_done = true;
            } else {
                t!(io::copy(&mut (&mut client).take(amt), &mut stderr));
                t!(stderr.flush());
            }
        }
    }

    // Finally, read out the exit status
    let mut status = [0; 5];
    t!(client.read_exact(&mut status));
    let code = ((status[1] as i32) << 24)
        | ((status[2] as i32) << 16)
        | ((status[3] as i32) << 8)
        | ((status[4] as i32) << 0);
    if status[0] == 0 {
        std::process::exit(code);
    } else {
        println!("died due to signal {}", code);
        std::process::exit(3);
    }
}

fn send(path: &Path, dst: &mut dyn Write) {
    t!(dst.write_all(path.file_name().unwrap().to_str().unwrap().as_bytes()));
    t!(dst.write_all(&[0]));
    let mut file = t!(File::open(&path));
    let amt = t!(file.metadata()).len();
    t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,]));
    t!(io::copy(&mut file, dst));
}
<commit_msg>Rollup merge of #72318 - tblah:remote-test-client-doc, r=nikomatsakis<commit_after>//! This is a small client program intended to pair with `remote-test-server` in
//! this repository. This client connects to the server over TCP and is used to
//! push artifacts and run tests on the server instead of locally.
//!
//! Here is also where we bake in the support to spawn the QEMU emulator as
//! well.

#![deny(warnings)]

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::TcpStream;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;

const REMOTE_ADDR_ENV: &str = "TEST_DEVICE_ADDR";
const DEFAULT_ADDR: &str = "127.0.0.1:12345";

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

fn main() {
    let mut args = env::args().skip(1);
    let next = args.next();
    if next.is_none() {
        return help();
    }

    match &next.unwrap()[..] {
        "spawn-emulator" => spawn_emulator(
            &args.next().unwrap(),
            Path::new(&args.next().unwrap()),
            Path::new(&args.next().unwrap()),
            args.next().map(|s| s.into()),
        ),
        "push" => push(Path::new(&args.next().unwrap())),
        "run" => run(args.next().unwrap(), args.collect()),
        "help" | "-h" | "--help" => help(),
        cmd => {
            println!("unknown command: {}", cmd);
            help();
        }
    }
}

fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option<PathBuf>) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());

    if env::var(REMOTE_ADDR_ENV).is_ok() {
        println!("Connecting to remote device {} ...", device_address);
    } else if target.contains("android") {
        start_android_emulator(server);
    } else {
        let rootfs = rootfs.as_ref().expect("need rootfs on non-android");
        start_qemu_emulator(target, rootfs, server, tmpdir);
    }

    // Wait for the emulator to come online
    loop {
        let dur = Duration::from_millis(100);
        if let Ok(mut client) = TcpStream::connect(&device_address) {
            t!(client.set_read_timeout(Some(dur)));
            t!(client.set_write_timeout(Some(dur)));
            if client.write_all(b"ping").is_ok() {
                let mut b = [0; 4];
                if client.read_exact(&mut b).is_ok() {
                    break;
                }
            }
        }
        thread::sleep(dur);
    }
}

fn start_android_emulator(server: &Path) {
    println!("waiting for device to come online");
    let status = Command::new("adb").arg("wait-for-device").status().unwrap();
    assert!(status.success());

    println!("pushing server");
    let status =
        Command::new("adb").arg("push").arg(server).arg("/data/tmp/testd").status().unwrap();
    assert!(status.success());

    println!("forwarding tcp");
    let status =
        Command::new("adb").arg("forward").arg("tcp:12345").arg("tcp:12345").status().unwrap();
    assert!(status.success());

    println!("executing server");
    Command::new("adb").arg("shell").arg("/data/tmp/testd").spawn().unwrap();
}

fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path) {
    // Generate a new rootfs image now that we've updated the test server
    // executable. This is the equivalent of:
    //
    //      find $rootfs -print 0 | cpio --null -o --format=newc > rootfs.img
    t!(fs::copy(server, rootfs.join("testd")));
    let rootfs_img = tmpdir.join("rootfs.img");
    let mut cmd = Command::new("cpio");
    cmd.arg("--null")
        .arg("-o")
        .arg("--format=newc")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(rootfs);
    let mut child = t!(cmd.spawn());
    let mut stdin = child.stdin.take().unwrap();
    let rootfs = rootfs.to_path_buf();
    thread::spawn(move || add_files(&mut stdin, &rootfs, &rootfs));
    t!(io::copy(&mut child.stdout.take().unwrap(), &mut t!(File::create(&rootfs_img))));
    assert!(t!(child.wait()).success());

    // Start up the emulator, in the background
    match target {
        "arm-unknown-linux-gnueabihf" => {
            let mut cmd = Command::new("qemu-system-arm");
            cmd.arg("-M")
                .arg("vexpress-a15")
                .arg("-m")
                .arg("1024")
                .arg("-kernel")
                .arg("/tmp/zImage")
                .arg("-initrd")
                .arg(&rootfs_img)
                .arg("-dtb")
                .arg("/tmp/vexpress-v2p-ca15-tc1.dtb")
                .arg("-append")
                .arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
                .arg("-nographic")
                .arg("-redir")
                .arg("tcp:12345::12345");
            t!(cmd.spawn());
        }
        "aarch64-unknown-linux-gnu" => {
            let mut cmd = Command::new("qemu-system-aarch64");
            cmd.arg("-machine")
                .arg("virt")
                .arg("-cpu")
                .arg("cortex-a57")
                .arg("-m")
                .arg("1024")
                .arg("-kernel")
                .arg("/tmp/Image")
                .arg("-initrd")
                .arg(&rootfs_img)
                .arg("-append")
                .arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
                .arg("-nographic")
                .arg("-netdev")
                .arg("user,id=net0,hostfwd=tcp::12345-:12345")
                .arg("-device")
                .arg("virtio-net-device,netdev=net0,mac=00:00:00:00:00:00");
            t!(cmd.spawn());
        }
        _ => panic!("cannot start emulator for: {}" < target),
    }

    fn add_files(w: &mut dyn Write, root: &Path, cur: &Path) {
        for entry in t!(cur.read_dir()) {
            let entry = t!(entry);
            let path = entry.path();
            let to_print = path.strip_prefix(root).unwrap();
            t!(write!(w, "{}\u{0}", to_print.to_str().unwrap()));
            if t!(entry.file_type()).is_dir() {
                add_files(w, root, &path);
            }
        }
    }
}

fn push(path: &Path) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
    let client = t!(TcpStream::connect(device_address));
    let mut client = BufWriter::new(client);
    t!(client.write_all(b"push"));
    send(path, &mut client);
    t!(client.flush());

    // Wait for an acknowledgement that all the data was received. No idea
    // why this is necessary, seems like it shouldn't be!
    let mut client = client.into_inner().unwrap();
    let mut buf = [0; 4];
    t!(client.read_exact(&mut buf));
    assert_eq!(&buf, b"ack ");
    println!("done pushing {:?}", path);
}

fn run(files: String, args: Vec<String>) {
    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
    let client = t!(TcpStream::connect(device_address));
    let mut client = BufWriter::new(client);
    t!(client.write_all(b"run "));

    // Send over the args
    for arg in args {
        t!(client.write_all(arg.as_bytes()));
        t!(client.write_all(&[0]));
    }
    t!(client.write_all(&[0]));

    // Send over env vars
    //
    // Don't send over *everything* though as some env vars are set by and used
    // by the client.
    for (k, v) in env::vars() {
        match &k[..] {
            "PATH" | "LD_LIBRARY_PATH" | "PWD" => continue,
            _ => {}
        }
        t!(client.write_all(k.as_bytes()));
        t!(client.write_all(&[0]));
        t!(client.write_all(v.as_bytes()));
        t!(client.write_all(&[0]));
    }
    t!(client.write_all(&[0]));

    // Send over support libraries
    let mut files = files.split(':');
    let exe = files.next().unwrap();
    for file in files.map(Path::new) {
        send(&file, &mut client);
    }
    t!(client.write_all(&[0]));

    // Send over the client executable as the last piece
    send(exe.as_ref(), &mut client);

    println!("uploaded {:?}, waiting for result", exe);

    // Ok now it's time to read all the output. We're receiving "frames"
    // representing stdout/stderr, so we decode all that here.
    let mut header = [0; 5];
    let mut stderr_done = false;
    let mut stdout_done = false;
    let mut client = t!(client.into_inner());
    let mut stdout = io::stdout();
    let mut stderr = io::stderr();
    while !stdout_done || !stderr_done {
        t!(client.read_exact(&mut header));
        let amt = ((header[1] as u64) << 24)
            | ((header[2] as u64) << 16)
            | ((header[3] as u64) << 8)
            | ((header[4] as u64) << 0);
        if header[0] == 0 {
            if amt == 0 {
                stdout_done = true;
            } else {
                t!(io::copy(&mut (&mut client).take(amt), &mut stdout));
                t!(stdout.flush());
            }
        } else {
            if amt == 0 {
                stderr_done = true;
            } else {
                t!(io::copy(&mut (&mut client).take(amt), &mut stderr));
                t!(stderr.flush());
            }
        }
    }

    // Finally, read out the exit status
    let mut status = [0; 5];
    t!(client.read_exact(&mut status));
    let code = ((status[1] as i32) << 24)
        | ((status[2] as i32) << 16)
        | ((status[3] as i32) << 8)
        | ((status[4] as i32) << 0);
    if status[0] == 0 {
        std::process::exit(code);
    } else {
        println!("died due to signal {}", code);
        std::process::exit(3);
    }
}

fn send(path: &Path, dst: &mut dyn Write) {
    t!(dst.write_all(path.file_name().unwrap().to_str().unwrap().as_bytes()));
    t!(dst.write_all(&[0]));
    let mut file = t!(File::open(&path));
    let amt = t!(file.metadata()).len();
    t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,]));
    t!(io::copy(&mut file, dst));
}

fn help() {
    println!(
        "
Usage: {0} <command> [<args>]

Sub-commands:
    spawn-emulator <target> <server> <tmpdir> [rootfs]   See below
    push <path>                                          Copy <path> to emulator
    run <files> [args...]                                Run program on emulator
    help                                                 Display help message

Spawning an emulator:

For Android <target>s, adb will push the <server>, set up TCP forwarding and run
the <server>. Otherwise qemu emulates the target using a rootfs image created in
<tmpdir> and generated from <rootfs> plus the <server> executable.
If {1} is set in the environment, this step is skipped.

Pushing a path to a running emulator:

A running emulator or adb device is connected to at the IP address and port in
the {1} environment variable or {2} if this isn't
specified. The file at <path> is sent to this target.

Executing commands on a running emulator:

First the target emulator/adb session is connected to as for pushing files. Next
the colon separated list of <files> is pushed to the target. Finally, the first
file in <files> is executed in the emulator, preserving the current environment.
That command's status code is returned.
",
        env::args().next().unwrap(),
        REMOTE_ADDR_ENV,
        DEFAULT_ADDR
    );
}
<|endoftext|>


<commit_before>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs, TcpStream};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, ErrorKind};
use std::marker::PhantomData;

use openssl::ssl::SslStream;

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// A simple and low-level HTTP client implementation
struct BaseClient<T: Read+Write, S: Open+Stream<T>> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
	inner_stream_type: PhantomData<T>
}

/// Client for secured HTTP
pub type HttpsClient = BaseClient<SslStream<TcpStream>, HttpsStream>;
/// Client for unsecured HTTP
pub type HttpClient = BaseClient<TcpStream, HttpStream>;

impl <T: Read+Write, S: Open+Stream<T>> BaseClient<T, S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::BaseClient;
	/// let mut client = BaseClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<BaseClient<T, S>, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
			inner_stream_type: PhantomData
		};
		return Ok(client);
	}

	/// Get a property from client permanent header
	pub fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	/// Set a property in permanent header
	pub fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	/// Remove a property from permanent header
	pub fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	/// Return an iterator over properties names from permanent header
	pub fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	/// Return an iterator over properties from permanent header
	pub fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	pub fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<T>, Error> {
		let hdr = self.update_properties(header);
		let mut stream = try!(self.connect());
		let mut w = stream.new_writer();
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	pub fn get_reply(&mut self) -> Result<HttpReply<T>, Error> {
		let stream = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(stream.new_reader());
	}

	pub fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<T>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}<commit_msg>Fixed doc test error<commit_after>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs, TcpStream};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, ErrorKind};
use std::marker::PhantomData;

use openssl::ssl::SslStream;

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// A simple and low-level HTTP client implementation
struct BaseClient<T: Read+Write, S: Open+Stream<T>> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
	inner_stream_type: PhantomData<T>
}

/// Client for secured HTTP
pub type HttpsClient = BaseClient<SslStream<TcpStream>, HttpsStream>;
/// Client for unsecured HTTP
pub type HttpClient = BaseClient<TcpStream, HttpStream>;

impl <T: Read+Write, S: Open+Stream<T>> BaseClient<T, S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::BaseClient;
	/// let mut client = HttpClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<BaseClient<T, S>, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
			inner_stream_type: PhantomData
		};
		return Ok(client);
	}

	/// Get a property from client permanent header
	pub fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	/// Set a property in permanent header
	pub fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	/// Remove a property from permanent header
	pub fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	/// Return an iterator over properties names from permanent header
	pub fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	/// Return an iterator over properties from permanent header
	pub fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	pub fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<T>, Error> {
		let hdr = self.update_properties(header);
		let mut stream = try!(self.connect());
		let mut w = stream.new_writer();
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	pub fn get_reply(&mut self) -> Result<HttpReply<T>, Error> {
		let stream = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(stream.new_reader());
	}

	pub fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<T>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}<|endoftext|>


<commit_before>extern crate hyper;

use std::thread;
use std::collections::HashMap;

use self::hyper::server;

use http::dispatcher::Dispatcher;
use http::app_manager::AppManager;

pub struct Server {
    pub thread: thread::JoinHandle<()>,
}

impl Server {
    pub fn create() -> Server {
        let thread = Server::create_thread();

        Server{
            thread: thread,
        }
    }

    fn create_thread() -> thread::JoinHandle<()> {
        thread::spawn(move || {
            let app_manager = AppManager::new();
            let dispatcher = Dispatcher::new(app_manager);

            server::Server::http("127.0.0.1:12044").unwrap().handle(dispatcher).unwrap();
        })
    }
}

<commit_msg>refactor: Remove unused import<commit_after>extern crate hyper;

use std::thread;

use self::hyper::server;

use http::dispatcher::Dispatcher;
use http::app_manager::AppManager;

pub struct Server {
    pub thread: thread::JoinHandle<()>,
}

impl Server {
    pub fn create() -> Server {
        let thread = Server::create_thread();

        Server{
            thread: thread,
        }
    }

    fn create_thread() -> thread::JoinHandle<()> {
        thread::spawn(move || {
            let app_manager = AppManager::new();
            let dispatcher = Dispatcher::new(app_manager);

            server::Server::http("127.0.0.1:12044").unwrap().handle(dispatcher).unwrap();
        })
    }
}

<|endoftext|>


<commit_before>//! Contains all serverbound packets
//!
//! See the Serverbound sections on http://wiki.vg/Protocol for information
//! about each of the packets.

use connection::Packet;
use errors::Result;
use read::*;
use write::*;
use {ClientState, u128, utils};

use std::fmt;
use std::io::Read;

use openssl::rsa::Rsa;

/* See packets.clj for information about this include */
include!("./.serverbound-enum.generated.rs");
include!("./.serverbound-packets.generated.rs");

/* Now come to the manual definitions of packets that don't fit into the
 * code generation */

impl Handshake {
    /// Get the next state as a ClientState, if the value is valid
    pub fn get_next_clientstate(&self) -> Option<ClientState> {
        match self.next_state {
            1 => Some(ClientState::Status),
            2 => Some(ClientState::Login),
            _ => None,
        }
    }
}

impl EncryptionResponse {
    pub fn get_decrypted_shared_secret(&self, key: &Rsa) -> Result<[u8; 16]> {
        let tmp = utils::rsa_decrypt(key, &self.shared_secret)?;
        if tmp.len() != 16 {
            bail!("Decrypted shared secret was not 16 bytes long");
        }
        let mut ret = [0; 16];
        for i in 0..16 {
            ret[i] = tmp[i];
        }
        Ok(ret)
    }
    pub fn get_decrypted_verify_token(&self, key: &Rsa) -> Result<Vec<u8>> {
        utils::rsa_decrypt(key, &self.verify_token)
    }
}

impl StatusRequest {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&StatusRequest::PACKET_ID, &mut ret)?;
        Ok(ret)
    }
    fn deserialize<R: Read>(_: &mut R) -> Result<ServerboundPacket> {
        Ok(ServerboundPacket::StatusRequest(StatusRequest {}))
    }
}

impl TabComplete {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&TabComplete::PACKET_ID, &mut ret)?;
        write_String(&self.text, &mut ret)?;
        write_bool(&self.assume_command, &mut ret)?;
        match self.looked_at_block {
            Some(x) => {
                write_bool(&true, &mut ret)?;
                write_position(&x, &mut ret)?;
            },
            None => {
                write_bool(&false, &mut ret)?;
            },
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let text = read_String(r)?;
        let assume_command = read_bool(r)?;
        let has_position = read_bool(r)?;
        let looked_at_block = if has_position {
            Some(read_position(r)?)
        } else {
            None
        };

        Ok(ServerboundPacket::TabComplete(TabComplete {
                                              text: text,
                                              assume_command: assume_command,
                                              looked_at_block: looked_at_block,
                                          }))
    }
}

impl UseEntity {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&UseEntity::PACKET_ID, &mut ret)?;
        write_varint(&self.target, &mut ret)?;
        write_varint(&self.action, &mut ret)?;
        match self.action {
            2 => {
                if let Some((x, y, z)) = self.location {
                    write_f32(&x, &mut ret)?;
                    write_f32(&y, &mut ret)?;
                    write_f32(&z, &mut ret)?;
                } else {
                    bail!("UseEntity had invalid values. Location was None even though action was 2.");
                }
            },
            _ => (),
        }

        match self.action {
            0 | 1 => {
                if let Some(x) = self.hand {
                    write_varint(&x, &mut ret)?;
                } else {
                    bail!("UseEntity had invalid values. Hand was none even though action was {}", self.action);
                }
            },
            _ => (),
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let target = read_varint(r)?;
        let action = read_varint(r)?;

        let location = if action == 2 {
            Some((read_f32(r)?, read_f32(r)?, read_f32(r)?))
        } else {
            None
        };

        let hand = if action == 0 || action == 1 {
            Some(read_varint(r)?)
        } else {
            None
        };
        Ok(ServerboundPacket::UseEntity(UseEntity {
                                            target: target,
                                            action: action,
                                            location: location,
                                            hand: hand,
                                        }))
    }
}

impl CraftingBookData {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&Self::PACKET_ID, &mut ret)?;

        if let Some(x) = self.displayed_recipe {
            write_varint(&0, &mut ret)?;
            write_i32(&x, &mut ret)?;
        } else if let Some((book_open, filter)) = self.crafting_book_status {
            write_varint(&1, &mut ret)?;
            write_bool(&book_open, &mut ret)?;
            write_bool(&filter, &mut ret)?;
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let typee = read_varint(r)?;
        let (recipe, status) = match typee {
            0 => (Some(read_i32(r)?), None),
            1 => (None, Some((read_bool(r)?, read_bool(r)?))),
            _ => bail!("CraftingBookData got invalid type {}", typee),
        };
        Ok(ServerboundPacket::CraftingBookData(CraftingBookData {
                                                   displayed_recipe: recipe,
                                                   crafting_book_status: status,
                                               }))
    }
}

impl AdvancementTab {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&Self::PACKET_ID, &mut ret)?;

        if let Some(ref tab_id) = self.tab_id {
            write_varint(&0, &mut ret)?;
            write_String(tab_id, &mut ret)?;
        } else {
            write_varint(&1, &mut ret)?;
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let action = read_varint(r)?;
        let tab_id = match action {
            0 => Some(read_String(r)?),
            1 => None,
            _ => bail!("Advancement Tab got invalid action {}", action),
        };
        Ok(ServerboundPacket::AdvancementTab(AdvancementTab {
                                                 tab_id: tab_id,
                                             }))
    }
}

impl EncryptionResponse {
    /// Create the EncryptionResponse packet from the unencrypted shared secret
    /// and verify token, and the server's public key in DER format.
    pub fn new_unencrypted(key: &[u8],
                           shared_secret: &[u8],
                           verify_token: &[u8])
                           -> Result<ServerboundPacket> {
        let ss_encrypted = utils::rsa_encrypt(key, shared_secret)?;
        let verify_encrypted = utils::rsa_encrypt(key, verify_token)?;

        Ok(EncryptionResponse::new(ss_encrypted, verify_encrypted))
    }
}
<commit_msg>Fix wrong login on UseEntity packet<commit_after>//! Contains all serverbound packets
//!
//! See the Serverbound sections on http://wiki.vg/Protocol for information
//! about each of the packets.

use connection::Packet;
use errors::Result;
use read::*;
use write::*;
use {ClientState, u128, utils};

use std::fmt;
use std::io::Read;

use openssl::rsa::Rsa;

/* See packets.clj for information about this include */
include!("./.serverbound-enum.generated.rs");
include!("./.serverbound-packets.generated.rs");

/* Now come to the manual definitions of packets that don't fit into the
 * code generation */

impl Handshake {
    /// Get the next state as a ClientState, if the value is valid
    pub fn get_next_clientstate(&self) -> Option<ClientState> {
        match self.next_state {
            1 => Some(ClientState::Status),
            2 => Some(ClientState::Login),
            _ => None,
        }
    }
}

impl EncryptionResponse {
    pub fn get_decrypted_shared_secret(&self, key: &Rsa) -> Result<[u8; 16]> {
        let tmp = utils::rsa_decrypt(key, &self.shared_secret)?;
        if tmp.len() != 16 {
            bail!("Decrypted shared secret was not 16 bytes long");
        }
        let mut ret = [0; 16];
        for i in 0..16 {
            ret[i] = tmp[i];
        }
        Ok(ret)
    }
    pub fn get_decrypted_verify_token(&self, key: &Rsa) -> Result<Vec<u8>> {
        utils::rsa_decrypt(key, &self.verify_token)
    }
}

impl StatusRequest {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&StatusRequest::PACKET_ID, &mut ret)?;
        Ok(ret)
    }
    fn deserialize<R: Read>(_: &mut R) -> Result<ServerboundPacket> {
        Ok(ServerboundPacket::StatusRequest(StatusRequest {}))
    }
}

impl TabComplete {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&TabComplete::PACKET_ID, &mut ret)?;
        write_String(&self.text, &mut ret)?;
        write_bool(&self.assume_command, &mut ret)?;
        match self.looked_at_block {
            Some(x) => {
                write_bool(&true, &mut ret)?;
                write_position(&x, &mut ret)?;
            },
            None => {
                write_bool(&false, &mut ret)?;
            },
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let text = read_String(r)?;
        let assume_command = read_bool(r)?;
        let has_position = read_bool(r)?;
        let looked_at_block = if has_position {
            Some(read_position(r)?)
        } else {
            None
        };

        Ok(ServerboundPacket::TabComplete(TabComplete {
                                              text: text,
                                              assume_command: assume_command,
                                              looked_at_block: looked_at_block,
                                          }))
    }
}

impl UseEntity {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&UseEntity::PACKET_ID, &mut ret)?;
        write_varint(&self.target, &mut ret)?;
        write_varint(&self.action, &mut ret)?;
        match self.action {
            2 => {
                if let Some((x, y, z)) = self.location {
                    write_f32(&x, &mut ret)?;
                    write_f32(&y, &mut ret)?;
                    write_f32(&z, &mut ret)?;
                } else {
                    bail!("UseEntity had invalid values. Location was None even though action was 2.");
                }
            },
            _ => (),
        }

        match self.action {
            0 | 2 => {
                if let Some(x) = self.hand {
                    write_varint(&x, &mut ret)?;
                } else {
                    bail!("UseEntity had invalid values. Hand was none even though action was {}", self.action);
                }
            },
            _ => (),
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let target = read_varint(r)?;
        let action = read_varint(r)?;

        let location = if action == 2 {
            Some((read_f32(r)?, read_f32(r)?, read_f32(r)?))
        } else {
            None
        };

        let hand = if action == 0 || action == 2 {
            Some(read_varint(r)?)
        } else {
            None
        };
        Ok(ServerboundPacket::UseEntity(UseEntity {
                                            target: target,
                                            action: action,
                                            location: location,
                                            hand: hand,
                                        }))
    }
}

impl CraftingBookData {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&Self::PACKET_ID, &mut ret)?;

        if let Some(x) = self.displayed_recipe {
            write_varint(&0, &mut ret)?;
            write_i32(&x, &mut ret)?;
        } else if let Some((book_open, filter)) = self.crafting_book_status {
            write_varint(&1, &mut ret)?;
            write_bool(&book_open, &mut ret)?;
            write_bool(&filter, &mut ret)?;
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let typee = read_varint(r)?;
        let (recipe, status) = match typee {
            0 => (Some(read_i32(r)?), None),
            1 => (None, Some((read_bool(r)?, read_bool(r)?))),
            _ => bail!("CraftingBookData got invalid type {}", typee),
        };
        Ok(ServerboundPacket::CraftingBookData(CraftingBookData {
                                                   displayed_recipe: recipe,
                                                   crafting_book_status: status,
                                               }))
    }
}

impl AdvancementTab {
    fn to_u8(&self) -> Result<Vec<u8>> {
        let mut ret = Vec::new();
        write_varint(&Self::PACKET_ID, &mut ret)?;

        if let Some(ref tab_id) = self.tab_id {
            write_varint(&0, &mut ret)?;
            write_String(tab_id, &mut ret)?;
        } else {
            write_varint(&1, &mut ret)?;
        }
        Ok(ret)
    }
    fn deserialize<R: Read>(r: &mut R) -> Result<ServerboundPacket> {
        let action = read_varint(r)?;
        let tab_id = match action {
            0 => Some(read_String(r)?),
            1 => None,
            _ => bail!("Advancement Tab got invalid action {}", action),
        };
        Ok(ServerboundPacket::AdvancementTab(AdvancementTab {
                                                 tab_id: tab_id,
                                             }))
    }
}

impl EncryptionResponse {
    /// Create the EncryptionResponse packet from the unencrypted shared secret
    /// and verify token, and the server's public key in DER format.
    pub fn new_unencrypted(key: &[u8],
                           shared_secret: &[u8],
                           verify_token: &[u8])
                           -> Result<ServerboundPacket> {
        let ss_encrypted = utils::rsa_encrypt(key, shared_secret)?;
        let verify_encrypted = utils::rsa_encrypt(key, verify_token)?;

        Ok(EncryptionResponse::new(ss_encrypted, verify_encrypted))
    }
}
<|endoftext|>


<commit_before>#![crate_name = "split"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Akira Hayakawa <ruby.wktk@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::io;
use std::num::Int;
use std::char;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "split";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let opts = [
        getopts::optopt("a", "suffix-length", "use suffixes of length N (default 2)", "N"),
        getopts::optopt("b", "bytes", "put SIZE bytes per output file", "SIZE"),
        getopts::optopt("C", "line-bytes", "put at most SIZE bytes of lines per output file", "SIZE"),
        getopts::optflag("d", "numeric-suffixes", "use numeric suffixes instead of alphabetic"),
        getopts::optopt("l", "lines", "put NUMBER lines per output file", "NUMBER"),
        getopts::optflag("", "verbose", "print a diagnostic just before each output file is opened"),
        getopts::optflag("h", "help", "display help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [INPUT [PREFIX]]", NAME);
        println!("");
        io::print(getopts::usage("Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is -, read standard input." , &opts).as_slice());
        println!("");
        println!("SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.");
        return 0;
    }

    if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
        return 0;
    }

    let mut settings = Settings {
        prefix: "".to_string(),
        numeric_suffix: false,
        suffix_length: 0,
        input: "".to_string(),
        strategy: "".to_string(),
        strategy_param: "".to_string(),
        verbose: false,
    };

    settings.numeric_suffix = if matches.opt_present("d") { true } else { false };

    settings.suffix_length = match matches.opt_str("a") {
        Some(n) => match n.as_slice().parse() {
            Some(m) => m,
            None => crash!(1, "cannot parse num")
        },
        None => 2
    };

    settings.verbose = if matches.opt_present("verbose") { true } else { false };

    settings.strategy = "l".to_string();
    settings.strategy_param = "1000".to_string();
    let strategies = vec!["b", "C", "l"];
    for e in strategies.iter() {
        match matches.opt_str(*e) {
            Some(a) => {
                if settings.strategy.as_slice() == "l" {
                    settings.strategy = e.to_string();
                    settings.strategy_param = a;
                } else {
                    crash!(1, "{}: cannot split in more than one way", NAME)
                }
            },
            None => {}
        }
    }

    let mut v = matches.free.iter();
    let (input, prefix) = match (v.next(), v.next()) {
        (Some(a), None) => (a.to_string(), "x".to_string()),
        (Some(a), Some(b)) => (a.to_string(), b.to_string()),
        (None, _) => ("-".to_string(), "x".to_string()),
    };
    settings.input = input;
    settings.prefix = prefix;

    split(&settings)
}

struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: uint,
    input: String,
    strategy: String,
    strategy_param: String,
    verbose: bool,
}

struct SplitControl {
    current_line: String, // Don't touch
    request_new_file: bool, // Splitter implementation requests new file
}

trait Splitter {
    // Factory pattern
    fn new(_hint: Option<Self>, &Settings) -> Box<Splitter>;

    // Consume the current_line and return the consumed string
    fn consume(&mut self, &mut SplitControl) -> String;
}

struct LineSplitter {
    saved_lines_to_write: uint,
    lines_to_write: uint,
}

impl Splitter for LineSplitter {
    fn new(_: Option<LineSplitter>, settings: &Settings) -> Box<Splitter> {
        let n = match settings.strategy_param.as_slice().parse() {
            Some(a) => a,
            _ => crash!(1, "invalid number of lines")
        };
        box LineSplitter {
            saved_lines_to_write: n,
            lines_to_write: n,
        } as Box<Splitter>
    }

    fn consume(&mut self, control: &mut SplitControl) -> String {
        self.lines_to_write -= 1;
        if self.lines_to_write == 0 {
            self.lines_to_write = self.saved_lines_to_write;
            control.request_new_file = true;
        }
        control.current_line.clone()
    }
}

struct ByteSplitter {
    saved_bytes_to_write: uint,
    bytes_to_write: uint,
}

impl Splitter for ByteSplitter {
    fn new(_: Option<ByteSplitter>, settings: &Settings) -> Box<Splitter> {
        let mut strategy_param : Vec<char> = settings.strategy_param.chars().collect();
        let suffix = strategy_param.pop().unwrap();
        let multiplier = match suffix {
            '0'...'9' => 1u,
            'b' => 512u,
            'k' => 1024u,
            'm' => 1024u * 1024u,
            _ => crash!(1, "invalid number of bytes")
        };
        let n = if suffix.is_alphabetic() {
            match String::from_chars(strategy_param.as_slice()).as_slice().parse::<uint>() {
                Some(a) => a,
                _ => crash!(1, "invalid number of bytes")
            }
        } else {
            match settings.strategy_param.as_slice().parse::<uint>() {
                Some(a) => a,
                _ => crash!(1, "invalid number of bytes")
            }
        };
        box ByteSplitter {
            saved_bytes_to_write: n * multiplier,
            bytes_to_write: n * multiplier,
        } as Box<Splitter>
    }

    fn consume(&mut self, control: &mut SplitControl) -> String {
        let line = control.current_line.clone();
        let n = std::cmp::min(line.as_slice().chars().count(), self.bytes_to_write);
        self.bytes_to_write -= n;
        if n == 0 {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
        }
        line.as_slice().slice(0, n).to_string()
    }
}

// (1, 3) -> "aab"
fn str_prefix(i: uint, width: uint) -> String {
    let mut c = "".to_string();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = Int::pow(26 as uint, w);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
fn num_prefix(i: uint, width: uint) -> String {
    let mut c = "".to_string();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = Int::pow(10 as uint, w);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> int {
    let mut reader = io::BufferedReader::new(
        if settings.input.as_slice() == "-" {
            box io::stdio::stdin_raw() as Box<Reader>
        } else {
            let r = match io::File::open(&Path::new(settings.input.clone())) {
                Ok(a) => a,
                Err(_) => crash!(1, "cannot open '{}' for reading: No such file or directory", settings.input)
            };
            box r as Box<Reader>
        }
    );

    let mut splitter: Box<Splitter> =
        match settings.strategy.as_slice() {
            "l" => Splitter::new(None::<LineSplitter>, settings),
            "b" => Splitter::new(None::<ByteSplitter>, settings),
            a @ _ => crash!(1, "strategy {} not supported", a)
        };

    let mut control = SplitControl {
        current_line: "".to_string(), // Request new line
        request_new_file: true, // Request new file
    };

    let mut writer = io::BufferedWriter::new(box io::stdio::stdout_raw() as Box<Writer>);
    let mut fileno = 0;
    loop {
        if control.current_line.as_slice().chars().count() == 0 {
            match reader.read_line() {
                Ok(a) => { control.current_line = a; }
                Err(_) =>  { break; }
            }
        }

        if control.request_new_file {
            let mut filename = settings.prefix.to_string();
            filename.push_str(if settings.numeric_suffix {
                num_prefix(fileno, settings.suffix_length)
            } else {
                str_prefix(fileno, settings.suffix_length)
            }.as_slice());

            if fileno != 0 {
                crash_if_err!(1, writer.flush());
            }
            fileno += 1;
            writer = io::BufferedWriter::new(box io::File::open_mode(&Path::new(filename.as_slice()), io::Open, io::Write) as Box<Writer>);
            control.request_new_file = false;
        }

        let consumed = splitter.consume(&mut control);
        crash_if_err!(1, writer.write_str(consumed.as_slice()));

        let advance = consumed.as_slice().chars().count();
        let clone = control.current_line.clone();
        let sl = clone.as_slice();
        control.current_line = sl.slice(advance, sl.chars().count()).to_string();
    }
    0
}
<commit_msg>C flag for split<commit_after>#![crate_name = "split"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Akira Hayakawa <ruby.wktk@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::io;
use std::num::Int;
use std::char;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "split";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let opts = [
        getopts::optopt("a", "suffix-length", "use suffixes of length N (default 2)", "N"),
        getopts::optopt("b", "bytes", "put SIZE bytes per output file", "SIZE"),
        getopts::optopt("C", "line-bytes", "put at most SIZE bytes of lines per output file", "SIZE"),
        getopts::optflag("d", "numeric-suffixes", "use numeric suffixes instead of alphabetic"),
        getopts::optopt("l", "lines", "put NUMBER lines per output file", "NUMBER"),
        getopts::optflag("", "verbose", "print a diagnostic just before each output file is opened"),
        getopts::optflag("h", "help", "display help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [INPUT [PREFIX]]", NAME);
        println!("");
        io::print(getopts::usage("Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is -, read standard input." , &opts).as_slice());
        println!("");
        println!("SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.");
        return 0;
    }

    if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
        return 0;
    }

    let mut settings = Settings {
        prefix: "".to_string(),
        numeric_suffix: false,
        suffix_length: 0,
        input: "".to_string(),
        strategy: "".to_string(),
        strategy_param: "".to_string(),
        verbose: false,
    };

    settings.numeric_suffix = if matches.opt_present("d") { true } else { false };

    settings.suffix_length = match matches.opt_str("a") {
        Some(n) => match n.as_slice().parse() {
            Some(m) => m,
            None => crash!(1, "cannot parse num")
        },
        None => 2
    };

    settings.verbose = if matches.opt_present("verbose") { true } else { false };

    settings.strategy = "l".to_string();
    settings.strategy_param = "1000".to_string();
    let strategies = vec!["b", "C", "l"];
    for e in strategies.iter() {
        match matches.opt_str(*e) {
            Some(a) => {
                if settings.strategy.as_slice() == "l" {
                    settings.strategy = e.to_string();
                    settings.strategy_param = a;
                } else {
                    crash!(1, "{}: cannot split in more than one way", NAME)
                }
            },
            None => {}
        }
    }

    let mut v = matches.free.iter();
    let (input, prefix) = match (v.next(), v.next()) {
        (Some(a), None) => (a.to_string(), "x".to_string()),
        (Some(a), Some(b)) => (a.to_string(), b.to_string()),
        (None, _) => ("-".to_string(), "x".to_string()),
    };
    settings.input = input;
    settings.prefix = prefix;

    split(&settings)
}

struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: uint,
    input: String,
    strategy: String,
    strategy_param: String,
    verbose: bool,
}

struct SplitControl {
    current_line: String, // Don't touch
    request_new_file: bool, // Splitter implementation requests new file
}

trait Splitter {
    // Factory pattern
    fn new(_hint: Option<Self>, &Settings) -> Box<Splitter>;

    // Consume the current_line and return the consumed string
    fn consume(&mut self, &mut SplitControl) -> String;
}

struct LineSplitter {
    saved_lines_to_write: uint,
    lines_to_write: uint,
}

impl Splitter for LineSplitter {
    fn new(_: Option<LineSplitter>, settings: &Settings) -> Box<Splitter> {
        let n = match settings.strategy_param.as_slice().parse() {
            Some(a) => a,
            _ => crash!(1, "invalid number of lines")
        };
        box LineSplitter {
            saved_lines_to_write: n,
            lines_to_write: n,
        } as Box<Splitter>
    }

    fn consume(&mut self, control: &mut SplitControl) -> String {
        self.lines_to_write -= 1;
        if self.lines_to_write == 0 {
            self.lines_to_write = self.saved_lines_to_write;
            control.request_new_file = true;
        }
        control.current_line.clone()
    }
}

struct ByteSplitter {
    saved_bytes_to_write: uint,
    bytes_to_write: uint,
    break_on_line_end: bool,
    require_whole_line: bool,
}

impl Splitter for ByteSplitter {
    fn new(_: Option<ByteSplitter>, settings: &Settings) -> Box<Splitter> {
        let mut strategy_param : Vec<char> = settings.strategy_param.chars().collect();
        let suffix = strategy_param.pop().unwrap();
        let multiplier = match suffix {
            '0'...'9' => 1u,
            'b' => 512u,
            'k' => 1024u,
            'm' => 1024u * 1024u,
            _ => crash!(1, "invalid number of bytes")
        };
        let n = if suffix.is_alphabetic() {
            match String::from_chars(strategy_param.as_slice()).as_slice().parse::<uint>() {
                Some(a) => a,
                _ => crash!(1, "invalid number of bytes")
            }
        } else {
            match settings.strategy_param.as_slice().parse::<uint>() {
                Some(a) => a,
                _ => crash!(1, "invalid number of bytes")
            }
        };
        box ByteSplitter {
            saved_bytes_to_write: n * multiplier,
            bytes_to_write: n * multiplier,
            break_on_line_end: if settings.strategy == "b" { false } else { true },
            require_whole_line: false,
        } as Box<Splitter>
    }

    fn consume(&mut self, control: &mut SplitControl) -> String {
        let line = control.current_line.clone();
        let n = std::cmp::min(line.as_slice().chars().count(), self.bytes_to_write);
        if self.require_whole_line && n < line.as_slice().chars().count() {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
            self.require_whole_line = false;
            return line.as_slice().slice(0, 0).to_string();
        }
        self.bytes_to_write -= n;
        if n == 0 {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
        }
        if self.break_on_line_end && n == line.as_slice().chars().count() {
            self.require_whole_line = self.break_on_line_end;
        }
        line.as_slice().slice(0, n).to_string()
    }
}

// (1, 3) -> "aab"
fn str_prefix(i: uint, width: uint) -> String {
    let mut c = "".to_string();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = Int::pow(26 as uint, w);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
fn num_prefix(i: uint, width: uint) -> String {
    let mut c = "".to_string();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = Int::pow(10 as uint, w);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> int {
    let mut reader = io::BufferedReader::new(
        if settings.input.as_slice() == "-" {
            box io::stdio::stdin_raw() as Box<Reader>
        } else {
            let r = match io::File::open(&Path::new(settings.input.clone())) {
                Ok(a) => a,
                Err(_) => crash!(1, "cannot open '{}' for reading: No such file or directory", settings.input)
            };
            box r as Box<Reader>
        }
    );

    let mut splitter: Box<Splitter> =
        match settings.strategy.as_slice() {
            "l" => Splitter::new(None::<LineSplitter>, settings),
            "b" | "C" => Splitter::new(None::<ByteSplitter>, settings),
            a @ _ => crash!(1, "strategy {} not supported", a)
        };

    let mut control = SplitControl {
        current_line: "".to_string(), // Request new line
        request_new_file: true, // Request new file
    };

    let mut writer = io::BufferedWriter::new(box io::stdio::stdout_raw() as Box<Writer>);
    let mut fileno = 0;
    loop {
        if control.current_line.as_slice().chars().count() == 0 {
            match reader.read_line() {
                Ok(a) => { control.current_line = a; }
                Err(_) =>  { break; }
            }
        }

        if control.request_new_file {
            let mut filename = settings.prefix.to_string();
            filename.push_str(if settings.numeric_suffix {
                num_prefix(fileno, settings.suffix_length)
            } else {
                str_prefix(fileno, settings.suffix_length)
            }.as_slice());

            if fileno != 0 {
                crash_if_err!(1, writer.flush());
            }
            fileno += 1;
            writer = io::BufferedWriter::new(box io::File::open_mode(&Path::new(filename.as_slice()), io::Open, io::Write) as Box<Writer>);
            control.request_new_file = false;
        }

        let consumed = splitter.consume(&mut control);
        crash_if_err!(1, writer.write_str(consumed.as_slice()));

        let advance = consumed.as_slice().chars().count();
        let clone = control.current_line.clone();
        let sl = clone.as_slice();
        control.current_line = sl.slice(advance, sl.chars().count()).to_string();
    }
    0
}
<|endoftext|>