<commit_before>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Decrease).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::Increase).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| {
                m.send_layout_message(LayoutMessage::IncreaseMaster).reapply_layout(w, c)
            });

    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });

    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("+10")); w });

    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("-10")); w });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        p.stdin.as_mut().unwrap().write_line(content.as_slice()).unwrap();
    });
}

<commit_msg>updated config<commit_after>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Decrease).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Increase).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::IncreaseMaster).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("+10")); w });
    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("-10")); w });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        p.stdin.as_mut().unwrap().write_line(content.as_slice()).unwrap();
    });
}

<|endoftext|>


<commit_before>// `cargo test --benches` and `#[feature(test)]` work only in nightly
#![cfg(rustc_nightly)]
#![feature(test)]

use std::sync::Arc;

extern crate bytes;
extern crate futures;
extern crate httpbis;
extern crate test;

use httpbis::*;

use futures::future::Future;
use futures::stream;
use futures::stream::Stream;

use bytes::Bytes;

use test::Bencher;

#[bench]
fn download_megabyte_in_kb_chunks(b: &mut Bencher) {
    b.iter(|| {
        struct Megabyte;

        impl Service for Megabyte {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::headers_and_bytes_stream(
                    Headers::ok_200(),
                    stream::iter_ok((0..1024).map(|i| Bytes::from(vec![(i % 0xff) as u8; 1024]))),
                )
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(Megabyte));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default(),
        ).expect("client");

        let (header, body) = client
            .start_get("/any", "localhost")
            .0
            .wait()
            .expect("headers");
        assert_eq!(200, header.status());

        let mut s = 0;
        for p in body.wait() {
            match p.expect("part") {
                DataOrTrailers::Data(d, ..) => s += d.len(),
                _ => panic!("unexpected headers"),
            }
        }

        assert_eq!(1024 * 1024, s);
    })
}

#[bench]
fn thousand_small_requests(b: &mut Bencher) {
    b.iter(|| {
        struct My;

        impl Service for My {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::found_200_plain_text("hello there")
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(My));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default(),
        ).expect("client");

        for _i in 0..1000 {
            let (header, body) = client
                .start_get("/any", "localhost")
                .0
                .wait()
                .expect("headers");
            assert_eq!(200, header.status());

            // TODO: check content
            body.collect().wait().expect("body");
        }
    })
}
<commit_msg>Use 100 requests instead of 1000 in bench<commit_after>// `cargo test --benches` and `#[feature(test)]` work only in nightly
#![cfg(rustc_nightly)]
#![feature(test)]

use std::sync::Arc;

extern crate bytes;
extern crate futures;
extern crate httpbis;
extern crate test;

use httpbis::*;

use futures::future::Future;
use futures::stream;
use futures::stream::Stream;

use bytes::Bytes;

use test::Bencher;

#[bench]
fn download_megabyte_in_kb_chunks(b: &mut Bencher) {
    b.iter(|| {
        struct Megabyte;

        impl Service for Megabyte {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::headers_and_bytes_stream(
                    Headers::ok_200(),
                    stream::iter_ok((0..1024).map(|i| Bytes::from(vec![(i % 0xff) as u8; 1024]))),
                )
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(Megabyte));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default(),
        ).expect("client");

        let (header, body) = client
            .start_get("/any", "localhost")
            .0
            .wait()
            .expect("headers");
        assert_eq!(200, header.status());

        let mut s = 0;
        for p in body.wait() {
            match p.expect("part") {
                DataOrTrailers::Data(d, ..) => s += d.len(),
                _ => panic!("unexpected headers"),
            }
        }

        assert_eq!(1024 * 1024, s);
    })
}

#[bench]
fn hundred_small_requests(b: &mut Bencher) {
    b.iter(|| {
        struct My;

        impl Service for My {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::found_200_plain_text("hello there")
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(My));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default(),
        ).expect("client");

        for _i in 0..100 {
            let (header, body) = client
                .start_get("/any", "localhost")
                .0
                .wait()
                .expect("headers");
            assert_eq!(200, header.status());

            // TODO: check content
            body.collect().wait().expect("body");
        }
    })
}
<|endoftext|>


<commit_before>extern crate libc;

use self::libc::c_char;
use std::ffi::{CString,CStr};
use std::mem;
use std::ptr;
use std::hash::{Hash,Hasher,SipHasher};
use super::Name;

#[no_mangle]
pub extern "C" fn human_name_parse(input: *const libc::c_char) -> Option<Box<Name>> {
    let s = unsafe {
        CStr::from_ptr(input).to_string_lossy()
    };
    Name::parse(&*s).map(|n| Box::new(n))
}

#[no_mangle]
pub extern "C" fn human_name_free_name(name_ptr: *mut Name) {
    unsafe {
        mem::drop(Box::from_raw(name_ptr));
    }
}

#[no_mangle]
pub extern "C" fn human_name_free_string(str_ptr: *mut c_char) {
    unsafe {
        mem::drop(CString::from_raw(str_ptr));
    }
}

#[no_mangle]
pub extern "C" fn human_name_consistent_with(a: &Name, b: &Name) -> bool {
    a.consistent_with(b)
}

#[no_mangle]
pub extern "C" fn human_name_hash(name: &Name) -> u64 {
    let mut s = SipHasher::new();
    name.hash(&mut s);
    s.finish()
}

#[no_mangle]
pub extern "C" fn human_name_surname(name: &Name) -> *const c_char {
    let s = CString::new(name.surname().into_owned()).unwrap();
    s.into_raw()
}

#[no_mangle]
pub extern "C" fn human_name_given_name(name: &Name) -> *const c_char {
    match name.given_name() {
        Some(given_name) => {
            let s = CString::new(given_name).unwrap();
            s.into_raw()
        }
        None => ptr::null()
    }
}

#[no_mangle]
pub extern "C" fn human_name_initials(name: &Name) -> *const c_char {
    let s = CString::new(name.initials()).unwrap();
    s.into_raw()
}

#[no_mangle]
pub extern "C" fn human_name_first_initial(name: &Name) -> *const c_char {
    let s = CString::new(name.first_initial().to_string()).unwrap();
    s.into_raw()
}

#[no_mangle]
pub extern "C" fn human_name_middle_initials(name: &Name) -> *const c_char {
    match name.middle_initials() {
        Some(initials) => {
            let s = CString::new(initials).unwrap();
            s.into_raw()
        }
        None => ptr::null()
    }
}

#[no_mangle]
pub extern "C" fn human_name_goes_by_middle_name(name: &Name) -> bool {
    name.goes_by_middle_name()
}

#[no_mangle]
pub extern "C" fn human_name_middle_names(name: &Name) -> *const c_char {
    match name.middle_name() {
        Some(middle_names) => {
            let s = CString::new(middle_names.into_owned()).unwrap();
            s.into_raw()
        }
        None => ptr::null()
    }
}

#[no_mangle]
pub extern "C" fn human_name_suffix(name: &Name) -> *const c_char {
    match name.suffix() {
        Some(suffix) => {
            let s = CString::new(suffix).unwrap();
            s.into_raw()
        }
        None => ptr::null()
    }
}

#[no_mangle]
pub extern "C" fn human_name_display_short(name: &Name) -> *const c_char {
    let s = CString::new(name.display_short()).unwrap();
    s.into_raw()
}

#[no_mangle]
pub extern "C" fn human_name_display_full(name: &Name) -> *const c_char {
    let s = CString::new(name.display_full()).unwrap();
    s.into_raw()
}
<commit_msg>DRY-up external.rs with macros<commit_after>extern crate libc;

use self::libc::c_char;
use std::ffi::{CString,CStr};
use std::mem;
use std::ptr;
use std::hash::{Hash,Hasher,SipHasher};
use super::Name;

macro_rules! str_to_char_star {
    ($str:expr) => { {
        let s = CString::new($str).unwrap();
        s.into_raw()
    } }
}

macro_rules! option_str_to_char_star {
    ($opt:expr) => {
        match $opt {
            Some(string) => {
                let s = CString::new(string).unwrap();
                s.into_raw()
            }
            None => ptr::null()
        }
    }
}

#[no_mangle]
pub extern "C" fn human_name_parse(input: *const libc::c_char) -> Option<Box<Name>> {
    let s = unsafe {
        CStr::from_ptr(input).to_string_lossy()
    };
    Name::parse(&*s).map(|n| Box::new(n))
}

#[no_mangle]
pub extern "C" fn human_name_free_name(name_ptr: *mut Name) {
    unsafe {
        mem::drop(Box::from_raw(name_ptr));
    }
}

#[no_mangle]
pub extern "C" fn human_name_free_string(str_ptr: *mut c_char) {
    unsafe {
        mem::drop(CString::from_raw(str_ptr));
    }
}

#[no_mangle]
pub extern "C" fn human_name_consistent_with(a: &Name, b: &Name) -> bool {
    a.consistent_with(b)
}

#[no_mangle]
pub extern "C" fn human_name_hash(name: &Name) -> u64 {
    let mut s = SipHasher::new();
    name.hash(&mut s);
    s.finish()
}

#[no_mangle]
pub extern "C" fn human_name_surname(name: &Name) -> *const c_char {
    str_to_char_star!(name.surname().into_owned())
}

#[no_mangle]
pub extern "C" fn human_name_given_name(name: &Name) -> *const c_char {
    option_str_to_char_star!(name.given_name())
}

#[no_mangle]
pub extern "C" fn human_name_initials(name: &Name) -> *const c_char {
    str_to_char_star!(name.initials())
}

#[no_mangle]
pub extern "C" fn human_name_first_initial(name: &Name) -> *const c_char {
    str_to_char_star!(name.first_initial().to_string())
}

#[no_mangle]
pub extern "C" fn human_name_middle_initials(name: &Name) -> *const c_char {
    option_str_to_char_star!(name.middle_initials())
}

#[no_mangle]
pub extern "C" fn human_name_goes_by_middle_name(name: &Name) -> bool {
    name.goes_by_middle_name()
}

#[no_mangle]
pub extern "C" fn human_name_middle_names(name: &Name) -> *const c_char {
    option_str_to_char_star!(name.middle_name().map(|n| n.into_owned()))
}

#[no_mangle]
pub extern "C" fn human_name_suffix(name: &Name) -> *const c_char {
    option_str_to_char_star!(name.suffix())
}

#[no_mangle]
pub extern "C" fn human_name_display_short(name: &Name) -> *const c_char {
    str_to_char_star!(name.display_short())
}

#[no_mangle]
pub extern "C" fn human_name_display_full(name: &Name) -> *const c_char {
    str_to_char_star!(name.display_full())
}
<|endoftext|>


<commit_before>use std::ops::Deref;
use std::marker::PhantomData;
use std::io;
use std::collections::BTreeMap;
use iron::IronError;
use iron::prelude::*;
use iron::status;
use iron::headers::Cookie;
use hyper::header::{ContentType, SetCookie};

use serde_json;
use cookie::Cookie as CookiePair;
use serde_json::value::ToJson;
use exonum::crypto::PublicKey;
use exonum::crypto::SecretKey;
use router::Router;

use serde::{Serialize, Serializer};
use serde::de;
use serde::de::{Visitor, Deserialize, Deserializer};

use exonum::events::Error as EventsError;
use exonum::crypto::{HexValue, FromHexError, Hash, ToHex};
use exonum::storage::{Result as StorageResult, Error as StorageError};

#[derive(Debug)]
pub enum ApiError {
    Storage(StorageError),
    Events(EventsError),
    FromHex(FromHexError),
    Io(::std::io::Error),
    FileNotFound(Hash),
    NotFound,
    FileToBig,
    FileExists(Hash),
    IncorrectRequest,
    Unauthorized,
}

impl ::std::fmt::Display for ApiError {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl ::std::error::Error for ApiError {
    fn description(&self) -> &str {
        match *self {
            ApiError::Storage(_) => "Storage",
            ApiError::Events(_) => "Events",
            ApiError::FromHex(_) => "FromHex",
            ApiError::Io(_) => "Io",
            ApiError::FileNotFound(_) => "FileNotFound",
            ApiError::NotFound => "NotFound",
            ApiError::FileToBig => "FileToBig",
            ApiError::FileExists(_) => "FileExists",
            ApiError::IncorrectRequest => "IncorrectRequest",
            ApiError::Unauthorized => "Unauthorized",
        }
    }
}

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

impl From<StorageError> for ApiError {
    fn from(e: StorageError) -> ApiError {
        ApiError::Storage(e)
    }
}

impl From<EventsError> for ApiError {
    fn from(e: EventsError) -> ApiError {
        ApiError::Events(e)
    }
}

impl From<FromHexError> for ApiError {
    fn from(e: FromHexError) -> ApiError {
        ApiError::FromHex(e)
    }
}

impl From<ApiError> for IronError {
    fn from(e: ApiError) -> IronError {
        use std::error::Error;

        let mut body = BTreeMap::new();
        body.insert("type", e.description().into());
        let code = match e {
            ApiError::FileExists(hash) => {
                body.insert("hash", ToHex::to_hex(&hash));
                status::Conflict
            }
            ApiError::FileNotFound(hash) => {
                body.insert("hash", ToHex::to_hex(&hash));
                status::Conflict
            }
            _ => status::Conflict,
        };
        IronError {
            error: Box::new(e),
            response: Response::with((code, body.to_json().to_string())),
        }
    }
}

#[derive(Clone, Debug)]
pub struct HexField<T: AsRef<[u8]> + Clone>(pub T);

impl<T> Deref for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> Serialize for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        ser.serialize_str(&self.0.as_ref().to_hex())
    }
}

struct HexVisitor<T>
    where T: AsRef<[u8]> + HexValue
{
    _p: PhantomData<T>,
}

impl<T> Visitor for HexVisitor<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    type Value = HexField<T>;

    fn visit_str<E>(&mut self, s: &str) -> Result<HexField<T>, E>
        where E: de::Error
    {
        let v = T::from_hex(s).map_err(|_| de::Error::custom("Invalid hex"))?;
        Ok(HexField(v))
    }
}

impl<T> Deserialize for HexField<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        deserializer.deserialize_str(HexVisitor { _p: PhantomData })
    }
}

pub trait Api {
    fn load_hex_value_from_cookie<'a>(&self,
                                      request: &'a Request,
                                      key: &str)
                                      -> StorageResult<Vec<u8>> {
        if let Some(&Cookie(ref cookies)) = request.headers.get() {
            for cookie in cookies.iter() {
                match CookiePair::parse(cookie.as_str()) {
                    Ok(c) => {
                        if c.name() == key {
                            if let Ok(value) = HexValue::from_hex(c.value()) {
                                return Ok(value);
                            }
                        }
                    }
                    Err(_) => {}
                }
            }
        }
        Err(StorageError::new(format!("Unable to find value with given key {}", key)))
    }

    fn load_keypair_from_cookies(&self,
                                 request: &Request)
                                 -> Result<(PublicKey, SecretKey), ApiError> {
        let public_key =
            PublicKey::from_slice(self.load_hex_value_from_cookie(&request, "public_key")?
                .as_ref());
        let secret_key =
            SecretKey::from_slice(self.load_hex_value_from_cookie(&request, "secret_key")?
                .as_ref());

        let public_key = public_key.ok_or(ApiError::Unauthorized)?;
        let secret_key = secret_key.ok_or(ApiError::Unauthorized)?;
        Ok((public_key, secret_key))
    }

    fn ok_response_with_cookies(&self,
                                json: &serde_json::Value,
                                cookies: Option<Vec<String>>)
                                -> IronResult<Response> {
        let mut resp = Response::with((status::Ok, json.as_str().unwrap()));
        resp.headers.set(ContentType::json());
        if let Some(cookies) = cookies {
            resp.headers.set(SetCookie(cookies));
        }
        Ok(resp)
    }

    fn ok_response(&self, json: &serde_json::Value) -> IronResult<Response> {
        self.ok_response_with_cookies(json, None)
    }

    fn wire<'b>(&self, router: &'b mut Router);
}<commit_msg>Fix bug with complex json values passed to ok_response_with_cookies in BlockchainExplorer::Api.<commit_after>use std::ops::Deref;
use std::marker::PhantomData;
use std::io;
use std::collections::BTreeMap;
use iron::IronError;
use iron::prelude::*;
use iron::status;
use iron::headers::Cookie;
use hyper::header::{ContentType, SetCookie};

use serde_json;
use cookie::Cookie as CookiePair;
use serde_json::value::ToJson;
use exonum::crypto::PublicKey;
use exonum::crypto::SecretKey;
use router::Router;

use serde::{Serialize, Serializer};
use serde::de;
use serde::de::{Visitor, Deserialize, Deserializer};

use exonum::events::Error as EventsError;
use exonum::crypto::{HexValue, FromHexError, Hash, ToHex};
use exonum::storage::{Result as StorageResult, Error as StorageError};

#[derive(Debug)]
pub enum ApiError {
    Storage(StorageError),
    Events(EventsError),
    FromHex(FromHexError),
    Io(::std::io::Error),
    FileNotFound(Hash),
    NotFound,
    FileToBig,
    FileExists(Hash),
    IncorrectRequest,
    Unauthorized,
}

impl ::std::fmt::Display for ApiError {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl ::std::error::Error for ApiError {
    fn description(&self) -> &str {
        match *self {
            ApiError::Storage(_) => "Storage",
            ApiError::Events(_) => "Events",
            ApiError::FromHex(_) => "FromHex",
            ApiError::Io(_) => "Io",
            ApiError::FileNotFound(_) => "FileNotFound",
            ApiError::NotFound => "NotFound",
            ApiError::FileToBig => "FileToBig",
            ApiError::FileExists(_) => "FileExists",
            ApiError::IncorrectRequest => "IncorrectRequest",
            ApiError::Unauthorized => "Unauthorized",
        }
    }
}

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

impl From<StorageError> for ApiError {
    fn from(e: StorageError) -> ApiError {
        ApiError::Storage(e)
    }
}

impl From<EventsError> for ApiError {
    fn from(e: EventsError) -> ApiError {
        ApiError::Events(e)
    }
}

impl From<FromHexError> for ApiError {
    fn from(e: FromHexError) -> ApiError {
        ApiError::FromHex(e)
    }
}

impl From<ApiError> for IronError {
    fn from(e: ApiError) -> IronError {
        use std::error::Error;

        let mut body = BTreeMap::new();
        body.insert("type", e.description().into());
        let code = match e {
            ApiError::FileExists(hash) => {
                body.insert("hash", ToHex::to_hex(&hash));
                status::Conflict
            }
            ApiError::FileNotFound(hash) => {
                body.insert("hash", ToHex::to_hex(&hash));
                status::Conflict
            }
            _ => status::Conflict,
        };
        IronError {
            error: Box::new(e),
            response: Response::with((code, body.to_json().to_string())),
        }
    }
}

#[derive(Clone, Debug)]
pub struct HexField<T: AsRef<[u8]> + Clone>(pub T);

impl<T> Deref for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> Serialize for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        ser.serialize_str(&self.0.as_ref().to_hex())
    }
}

struct HexVisitor<T>
    where T: AsRef<[u8]> + HexValue
{
    _p: PhantomData<T>,
}

impl<T> Visitor for HexVisitor<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    type Value = HexField<T>;

    fn visit_str<E>(&mut self, s: &str) -> Result<HexField<T>, E>
        where E: de::Error
    {
        let v = T::from_hex(s).map_err(|_| de::Error::custom("Invalid hex"))?;
        Ok(HexField(v))
    }
}

impl<T> Deserialize for HexField<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        deserializer.deserialize_str(HexVisitor { _p: PhantomData })
    }
}

pub trait Api {
    fn load_hex_value_from_cookie<'a>(&self,
                                      request: &'a Request,
                                      key: &str)
                                      -> StorageResult<Vec<u8>> {
        if let Some(&Cookie(ref cookies)) = request.headers.get() {
            for cookie in cookies.iter() {
                match CookiePair::parse(cookie.as_str()) {
                    Ok(c) => {
                        if c.name() == key {
                            if let Ok(value) = HexValue::from_hex(c.value()) {
                                return Ok(value);
                            }
                        }
                    }
                    Err(_) => {}
                }
            }
        }
        Err(StorageError::new(format!("Unable to find value with given key {}", key)))
    }

    fn load_keypair_from_cookies(&self,
                                 request: &Request)
                                 -> Result<(PublicKey, SecretKey), ApiError> {
        let public_key =
            PublicKey::from_slice(self.load_hex_value_from_cookie(&request, "public_key")?
                .as_ref());
        let secret_key =
            SecretKey::from_slice(self.load_hex_value_from_cookie(&request, "secret_key")?
                .as_ref());

        let public_key = public_key.ok_or(ApiError::Unauthorized)?;
        let secret_key = secret_key.ok_or(ApiError::Unauthorized)?;
        Ok((public_key, secret_key))
    }

    fn ok_response_with_cookies(&self,
                                json: &serde_json::Value,
                                cookies: Option<Vec<String>>)
                                -> IronResult<Response> {
        let mut resp = Response::with((status::Ok, serde_json::to_string(json).unwrap()));
        resp.headers.set(ContentType::json());
        if let Some(cookies) = cookies {
            resp.headers.set(SetCookie(cookies));
        }
        Ok(resp)
    }

    fn ok_response(&self, json: &serde_json::Value) -> IronResult<Response> {
        self.ok_response_with_cookies(json, None)
    }

    fn wire<'b>(&self, router: &'b mut Router);
}

#[cfg(test)]
mod tests {
    use router::Router;
    use exonum::blockchain::Block;
    use exonum::crypto::Hash;
    use time::get_time;
    use serde_json;
    use super::Api;

    #[test]
    fn test_json_response_for_complex_val() {
        let str_val = "sghdkgskgskldghshgsd";
        let complex_val = Block::new(24,
                                     2,
                                     get_time(),
                                     &Hash::new([24; 32]),
                                     &Hash::new([34; 32]),
                                     &Hash::new([38; 32]));
        struct SampleAPI;
        impl Api for SampleAPI {
            fn wire<'b>(&self, _: &'b mut Router) {
                return;
            }
        }
        let stub = SampleAPI;
        let result = stub.ok_response(&serde_json::to_value(str_val));
        assert!(result.is_ok());
        let result = stub.ok_response(&serde_json::to_value(complex_val));
        assert!(result.is_ok());
        print!("{:?}", result);
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/listxattr.2.html
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

pub fn ext_attr_value<P, S>(path: P, name: S) -> std::io::Result<Vec<u8>>
where
    P: AsRef<Path>,
    S: AsRef<std::ffi::OsStr>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/getxattr.2.html
        fn lgetxattr(
            path: *const libc::c_char,
            name: *const libc::c_char,
            value: *mut libc::c_void,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    let c_str_name = std::ffi::CString::new(name.as_ref().as_bytes())
        // While `name` as returned by the `ext_attr_names` function cannot have
        // null bytes inside, we cannot guarantee that the user doesn't supply
        // a bogus string here. Thus, we have to do proper error handling here.
        .map_err(|error| {
            std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
        })?;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. The rest is just is a FFI call respecting
    // the spec.
    let len = unsafe {
        // First we call `lgetxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];
    let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. For remaining parameters we provide a buffer
    // with length obtained through the previous call and we explicitly supply
    // its length as well. In case the result length increases between the calls
    // the system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `lgetxattr` with the actual buffer of the size we
        // determined by the previous call.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), buf_ptr, buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // In very rare cases (if the users tampers with the file inbetween calls to
    // the `lgetxattr`), the length value can decrease. To avoid returning any
    // garbage at the end, we have to truncate the buffer to its real length.
    buf.truncate(len as usize);
    Ok(buf)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[test]
    fn ext_attr_value_not_existing() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let error = ext_attr_value(tempfile.path(), "user.foo").unwrap_err();
        assert_eq!(error.raw_os_error(), Some(libc::ENODATA));
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"bar");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"bar");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"quux");
        setfattr(tempfile.path(), "user.bar", b"norf");

        let foo_value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(foo_value, b"quux");

        let bar_value = ext_attr_value(tempfile.path(), "user.bar").unwrap();
        assert_eq!(bar_value, b"norf");
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<commit_msg>Add `lgetxattr` wrapper test with weird bytes.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Linux-specific utilities for working with the filesystem.

use std::path::Path;

// TODO: Document behaviour for symlinks.
/// Collects extended flags of the specified file.
///
/// The returned mask represents file attributes specific to the Linux extended
/// file system. Normally, they can be inspected using the `lsattr` command and
/// can be set through the `chattr` command.
///
/// See the [Wikipedia] article and the [man] page for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Chattr
/// [man]: https://linux.die.net/man/1/chattr
///
/// # Examples
///
/// ```no_run
/// const FS_FL_USER_VISIBLE: u32 = 0x0003DFFF;
///
/// let flags = rrg::fs::linux::flags("/tmp/foo").unwrap();
/// assert_eq!(flags & FS_FL_USER_VISIBLE, 0);
/// ```
pub fn flags<P>(path: P) -> std::io::Result<u32> where
    P: AsRef<Path>
{
    let file = std::fs::File::open(path)?;

    let mut flags = 0;
    let code = unsafe {
        // This block is safe: we simply pass a raw file descriptor (that is
        // valid until the end of the scope of this function) because this is
        // what the low-level API expects.
        use std::os::unix::io::AsRawFd as _;
        ioctls::fs_ioc_getflags(file.as_raw_fd(), &mut flags)
    };

    if code == 0 {
        Ok(flags as u32)
    } else {
        Err(std::io::Error::from_raw_os_error(code))
    }
}

pub fn ext_attr_names<P>(path: P) -> std::io::Result<Vec<std::ffi::OsString>>
where
    P: AsRef<Path>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/listxattr.2.html
        fn llistxattr(
            path: *const libc::c_char,
            list: *mut libc::c_char,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. The rest is just is a FFI call respecting the spec.
    let len = unsafe {
        // First we call `llistxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        llistxattr(c_str_path.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];

    // SAFETY: The correctness of the path is guaranteed by conversion to the
    // `CString` type above. For remaining parameters we provide a buffer with
    // length obtained through the previous call and we explicitly supply its
    // length as well. In case the result length increases between the calls the
    // system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `llistxattr` with the actual buffer of the size we
        // determined by the previous call.
        llistxattr(c_str_path.as_ptr(), buf.as_mut_ptr(), buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // It is possible that the `len` value is smaller than as returned by the
    // first `llistxattr` call if somebody tampered with the file in the mean-
    // time. To avoid having spurious empty slices (as each following null byte
    // would cause the `split_inclusive` iterator to yield one), we trim the
    // buffer to the acutally returned length.
    //
    // Note that it is impossible for the `len` value to increase, as otherwise
    // the `llistxattr` would fail.
    let result = buf[..len as usize]
        .split_inclusive(|byte| *byte == 0)
        .map(|slice| {
            // SAFETY: The slice is guaranteed to be terminated with a null byte
            // and not contain any null bytes elsewhere because of the splitting
            // above. This holds true also for the last slice provided by the
            // iterator.
            let c_str = unsafe {
                std::ffi::CStr::from_ptr(slice.as_ptr())
            };

            std::ffi::OsStr::from_bytes(c_str.to_bytes()).to_os_string()
        })
        .collect();

    Ok(result)
}

pub fn ext_attr_value<P, S>(path: P, name: S) -> std::io::Result<Vec<u8>>
where
    P: AsRef<Path>,
    S: AsRef<std::ffi::OsStr>,
{
    extern "C" {
        // https://man7.org/linux/man-pages/man2/getxattr.2.html
        fn lgetxattr(
            path: *const libc::c_char,
            name: *const libc::c_char,
            value: *mut libc::c_void,
            size: libc::size_t,
        ) -> libc::ssize_t;
    }

    use std::os::unix::ffi::OsStrExt as _;

    let os_str_path = path.as_ref().as_os_str();
    let c_str_path = std::ffi::CString::new(os_str_path.as_bytes())
        // It is not possible to have a null byte in a Linux path.
        .expect("path with a null character");

    let c_str_name = std::ffi::CString::new(name.as_ref().as_bytes())
        // While `name` as returned by the `ext_attr_names` function cannot have
        // null bytes inside, we cannot guarantee that the user doesn't supply
        // a bogus string here. Thus, we have to do proper error handling here.
        .map_err(|error| {
            std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
        })?;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. The rest is just is a FFI call respecting
    // the spec.
    let len = unsafe {
        // First we call `lgetxattr` with empty buffer to get the size of the
        // buffer that will collect the actual results.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), std::ptr::null_mut(), 0)
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    let mut buf = vec![0; len as usize];
    let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;

    // SAFETY: The correctness of the path and name is guaranteed by conversion
    // to the `CString` type above. For remaining parameters we provide a buffer
    // with length obtained through the previous call and we explicitly supply
    // its length as well. In case the result length increases between the calls
    // the system will report an error but will not cause memory issues.
    let len = unsafe {
        // Now we can call `lgetxattr` with the actual buffer of the size we
        // determined by the previous call.
        lgetxattr(c_str_path.as_ptr(), c_str_name.as_ptr(), buf_ptr, buf.len())
    };
    if len < 0 {
        return Err(std::io::Error::last_os_error());
    }

    // In very rare cases (if the users tampers with the file inbetween calls to
    // the `lgetxattr`), the length value can decrease. To avoid returning any
    // garbage at the end, we have to truncate the buffer to its real length.
    buf.truncate(len as usize);
    Ok(buf)
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    // TODO: Write tests for symlinks.

    #[test]
    fn test_flags_non_existing() {
        let tempdir = tempfile::tempdir().unwrap();

        assert!(flags(tempdir.path().join("foo")).is_err());
    }

    #[test]
    fn test_flags_noatime() {
        // https://elixir.bootlin.com/linux/v5.8.14/source/include/uapi/linux/fs.h#L245
        const FS_NOATIME_FL: std::os::raw::c_long = 0x00000080;

        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = File::create(tempdir.path().join("foo")).unwrap();

        unsafe {
            use std::os::unix::io::AsRawFd as _;
            let fd = tempfile.as_raw_fd();

            assert_eq!(ioctls::fs_ioc_setflags(fd, &FS_NOATIME_FL), 0);
        }

        let flags = flags(tempdir.path().join("foo")).unwrap();
        assert_eq!(flags & FS_NOATIME_FL as u32, FS_NOATIME_FL as u32);
    }

    #[test]
    fn ext_attr_names_none() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert!(ext_attr_names.is_empty());
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo"]);
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_names_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"");
        setfattr(tempfile.path(), "user.bar", b"");
        setfattr(tempfile.path(), "user.baz", b"");

        let ext_attr_names = ext_attr_names(tempfile.path()).unwrap();
        assert_eq!(ext_attr_names, vec!["user.foo", "user.bar", "user.baz"]);
    }

    #[test]
    fn ext_attr_value_not_existing() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();

        let error = ext_attr_value(tempfile.path(), "user.foo").unwrap_err();
        assert_eq!(error.raw_os_error(), Some(libc::ENODATA));
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"bar");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"bar");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_single_not_unicode() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"\xff\xfe\xff");

        let value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(value, b"\xff\xfe\xff");
    }

    #[cfg(feature = "test-setfattr")]
    #[test]
    fn ext_attr_value_multiple() {
        let tempfile = tempfile::NamedTempFile::new().unwrap();
        setfattr(tempfile.path(), "user.foo", b"quux");
        setfattr(tempfile.path(), "user.bar", b"norf");

        let foo_value = ext_attr_value(tempfile.path(), "user.foo").unwrap();
        assert_eq!(foo_value, b"quux");

        let bar_value = ext_attr_value(tempfile.path(), "user.bar").unwrap();
        assert_eq!(bar_value, b"norf");
    }

    #[cfg(feature = "test-setfattr")]
    fn setfattr<P, S>(path: P, name: S, value: &[u8])
    where
        P: AsRef<std::path::Path>,
        S: AsRef<std::ffi::OsStr>,
    {
        use std::os::unix::ffi::OsStrExt as _;

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg(name)
                .arg("--value").arg(std::ffi::OsStr::from_bytes(value))
                .arg(path.as_ref().as_os_str())
                .status()
                .unwrap()
                .success()
        };
    }
}
<|endoftext|>


<commit_before>use editor::Editor;
use redraw::RedrawTask;
use mode::Mode;
use mode::PrimitiveMode;
use mode::CommandMode;
use buffer::Buffer;

#[cfg(feature = "orbital")]
use orbclient::Color;

#[cfg(feature = "ansi")]
mod terminal {
	extern crate libc;

    use std::mem;
    use self::libc::{c_int, c_uint, c_ushort, c_uchar, STDOUT_FILENO};
    use self::libc::ioctl;

    use std::io::stdout;
    use std::io::prelude::*;

    extern {
        static tiocgwinsz: c_int;

		fn tcgetattr(filedes: c_int, termptr: *mut Termios) -> c_int;
		fn tcsetattr(filedes: c_int, opt: c_int, termptr: *mut Termios) -> c_int;
		fn cfmakeraw(termptr: *mut Termios);
    }

    #[repr(C)]
    struct TermSize {
        row: c_ushort,
        col: c_ushort,
        _x: c_ushort,
        _y: c_ushort,
    }

    pub fn termsize() -> Option<(usize, usize)> {
		unsafe {
			let mut size: TermSize = mem::zeroed();

			if ioctl(STDOUT_FILENO, tiocgwinsz as u64, &mut size as *mut _) == 0 {
				Some((size.col as usize, size.row as usize))
			} else {
				None
			}
		}
	}

    #[derive(Clone)]
	struct Termios {
		c_iflag: c_uint,
		c_oflag: c_uint,
		c_cflag: c_uint,
		c_lflag: c_uint,
		c_line: c_uchar,
		c_cc: [c_uchar; 32],
		c_ispeed: c_uint,
		c_ospeed: c_uint,
	}

	fn get_terminal_attr() -> (Termios, c_int) {
		unsafe {
			let mut ios = Termios {
				c_iflag: 0,
				c_oflag: 0,
				c_cflag: 0,
				c_lflag: 0,
				c_line: 0,
				c_cc: [0; 32],
				c_ispeed: 0,
				c_ospeed: 0
			};

			(ios, tcgetattr(0, &mut ios))
		}
	}

	fn make_raw(ios: &mut Termios) {
		unsafe {
			cfmakeraw(&mut *ios);
		}
	}

	fn set_terminal_attr(ios: *mut Termios) -> c_int {
		unsafe {
			tcsetattr(0, 0, ios)
		}
	}

	pub struct TerminalRestorer {
		prev_ios: Termios
	}

	impl Drop for TerminalRestorer {
		fn drop(&mut self) {
			set_terminal_attr(&mut self.prev_ios as *mut _);
		}
	}

	pub fn set_terminal_raw_mode() -> TerminalRestorer {
		let (ios, err) = get_terminal_attr();
        let prev_ios = ios.clone();
		if err != 0 {
			panic!("Can't load termios settings properly");
		}

		make_raw(&mut ios);

		if set_terminal_attr(&mut ios as *mut _) != 0 {
			panic!("Can't init termios raw mode properly");
		}

		TerminalRestorer {
			prev_ios: prev_ios,
		}
	}

    pub fn csi(b: &[u8]) {
        let stdout = stdout();

        stdout.write(b"\x1B[");
        stdout.write(b);
        stdout.flush();
    }

    pub fn print(b: &[u8]) {
        let stdout = stdout();

        stdout.write(b);
        stdout.flush();
    }

    pub fn clear() {
        csi(b"2J");
    }

    pub fn show_cursor() {
        csi(b"?25h");
    }

    pub fn hide_cursor() {
        csi(b"?25l");
    }

    pub fn reset_style() {
        csi(b"0m");
    }

    pub fn move_cursor(x: usize, y: usize) {
        csi(&[]);
        print(y.to_string().as_bytes());
        print(b";");
        print(x.to_string().as_bytes());
        print(b"H");
    }
}

#[cfg(feature = "orbital")]
impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let (mut pos_x, pos_y) = self.pos();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        let w = self.window.width();

        if self.options.line_marker {
            self.window.rect(0,
                             (pos_y - self.scroll_y) as i32 * 16,
                             w,
                             16,
                             Color::rgb(45, 45, 45));
        }

        self.window.rect(8 * (pos_x - self.scroll_x) as i32,
                         16 * (pos_y - self.scroll_y) as i32,
                         8,
                         16,
                         Color::WHITE);

        let mut string = false;

        for (y, row) in self.buffer.lines().enumerate() {
            for (x, c) in row.chars().enumerate() {
                // TODO: Move outta here
                let color = if self.options.highlight {
                    match c {
                        '\'' | '"' => {
                            string = !string;
                            (226, 225, 167) //(167, 222, 156)
                        }
                        _ if string => (226, 225, 167), //(167, 222, 156)
                        '!' |
                        '@' |
                        '#' |
                        '$' |
                        '%' |
                        '^' |
                        '&' |
                        '|' |
                        '*' |
                        '+' |
                        '-' |
                        '/' |
                        ':' |
                        '=' |
                        '<' |
                        '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                        '.' | ',' => (241, 213, 226),
                        '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                        '0' ... '9' => (209, 209, 177),
                        _ => (255, 255, 255),
                    }
                } else {
                    (255, 255, 255)
                };

                let c = if c == '\t' {
                    ' '
                } else {
                    c
                };

                if pos_x == x && pos_y == y {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }

        self.redraw_task = RedrawTask::Null;

        self.redraw_status_bar();
        self.window.sync();
    }

    /// Redraw the status bar
    pub fn redraw_status_bar(&mut self) {
        let h = self.window.height();
        let w = self.window.width();
        let mode = self.cursor().mode;
        self.window.rect(0,
                         h as i32 - 18 -
                         {
                             if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                 18
                             } else {
                                 0
                             }
                         },
                         w,
                         18,
                         Color::rgba(74, 74, 74, 255));

        let sb_mode = self.status_bar.mode.clone();
        status_bar(self, sb_mode, 0, 4);
        let sb_file = self.status_bar.file.clone();
        status_bar(self, sb_file, 1, 4);
        let sb_cmd = self.status_bar.cmd.clone();
        status_bar(self, sb_cmd, 2, 4);
        let sb_msg = self.status_bar.msg.clone();
        status_bar(self, sb_msg, 3, 4);

        for (n, c) in self.prompt.chars().enumerate() {
            self.window.char(n as i32 * 8, h as i32 - 16 - 1, c, Color::WHITE);
        }

        self.window.sync();
    }
}

#[cfg(feature = "orbital")]
fn status_bar(editor: &mut Editor, text: String, a: u32, b: u32) {

    let h = editor.window.height();
    let w = editor.window.width();
    // let y = editor.y();
    let mode = editor.cursor().mode;

    for (n, c) in (if text.len() as u32 > w / (8 * b) {
                      text.chars().take((w / (8 * b) - 5) as usize).chain(vec!['.'; 3]).collect::<Vec<_>>()
                  } else {
                      text.chars().collect()
                  })
                  .into_iter()
                  .enumerate() {

        editor.window.char(((w * a) / b) as i32 + (n as i32 * 8),
                           h as i32 - 16 - 1 -
                           {
                               if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                   16 + 1 + 1
                               } else {
                                   0
                               }
                           },
                           c,
                           Color::WHITE);
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: "Welcome to Sodium!".to_string(),
        }
    }
}
<commit_msg>Remove color constant for new orbital<commit_after>use editor::Editor;
use redraw::RedrawTask;
use mode::Mode;
use mode::PrimitiveMode;
use mode::CommandMode;
use buffer::Buffer;

#[cfg(feature = "orbital")]
use orbclient::Color;

#[cfg(feature = "ansi")]
mod terminal {
	extern crate libc;

    use std::mem;
    use self::libc::{c_int, c_uint, c_ushort, c_uchar, STDOUT_FILENO};
    use self::libc::ioctl;

    use std::io::stdout;
    use std::io::prelude::*;

    extern {
        static tiocgwinsz: c_int;

		fn tcgetattr(filedes: c_int, termptr: *mut Termios) -> c_int;
		fn tcsetattr(filedes: c_int, opt: c_int, termptr: *mut Termios) -> c_int;
		fn cfmakeraw(termptr: *mut Termios);
    }

    #[repr(C)]
    struct TermSize {
        row: c_ushort,
        col: c_ushort,
        _x: c_ushort,
        _y: c_ushort,
    }

    pub fn termsize() -> Option<(usize, usize)> {
		unsafe {
			let mut size: TermSize = mem::zeroed();

			if ioctl(STDOUT_FILENO, tiocgwinsz as u64, &mut size as *mut _) == 0 {
				Some((size.col as usize, size.row as usize))
			} else {
				None
			}
		}
	}

    #[derive(Clone)]
	struct Termios {
		c_iflag: c_uint,
		c_oflag: c_uint,
		c_cflag: c_uint,
		c_lflag: c_uint,
		c_line: c_uchar,
		c_cc: [c_uchar; 32],
		c_ispeed: c_uint,
		c_ospeed: c_uint,
	}

	fn get_terminal_attr() -> (Termios, c_int) {
		unsafe {
			let mut ios = Termios {
				c_iflag: 0,
				c_oflag: 0,
				c_cflag: 0,
				c_lflag: 0,
				c_line: 0,
				c_cc: [0; 32],
				c_ispeed: 0,
				c_ospeed: 0
			};

			(ios, tcgetattr(0, &mut ios))
		}
	}

	fn make_raw(ios: &mut Termios) {
		unsafe {
			cfmakeraw(&mut *ios);
		}
	}

	fn set_terminal_attr(ios: *mut Termios) -> c_int {
		unsafe {
			tcsetattr(0, 0, ios)
		}
	}

	pub struct TerminalRestorer {
		prev_ios: Termios
	}

	impl Drop for TerminalRestorer {
		fn drop(&mut self) {
			set_terminal_attr(&mut self.prev_ios as *mut _);
		}
	}

	pub fn set_terminal_raw_mode() -> TerminalRestorer {
		let (ios, err) = get_terminal_attr();
        let prev_ios = ios.clone();
		if err != 0 {
			panic!("Can't load termios settings properly");
		}

		make_raw(&mut ios);

		if set_terminal_attr(&mut ios as *mut _) != 0 {
			panic!("Can't init termios raw mode properly");
		}

		TerminalRestorer {
			prev_ios: prev_ios,
		}
	}

    pub fn csi(b: &[u8]) {
        let stdout = stdout();

        stdout.write(b"\x1B[");
        stdout.write(b);
        stdout.flush();
    }

    pub fn print(b: &[u8]) {
        let stdout = stdout();

        stdout.write(b);
        stdout.flush();
    }

    pub fn clear() {
        csi(b"2J");
    }

    pub fn show_cursor() {
        csi(b"?25h");
    }

    pub fn hide_cursor() {
        csi(b"?25l");
    }

    pub fn reset_style() {
        csi(b"0m");
    }

    pub fn move_cursor(x: usize, y: usize) {
        csi(&[]);
        print(y.to_string().as_bytes());
        print(b";");
        print(x.to_string().as_bytes());
        print(b"H");
    }
}

#[cfg(feature = "orbital")]
impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let (mut pos_x, pos_y) = self.pos();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        let w = self.window.width();

        if self.options.line_marker {
            self.window.rect(0,
                             (pos_y - self.scroll_y) as i32 * 16,
                             w,
                             16,
                             Color::rgb(45, 45, 45));
        }

        self.window.rect(8 * (pos_x - self.scroll_x) as i32,
                         16 * (pos_y - self.scroll_y) as i32,
                         8,
                         16,
                         Color::rgb(255, 255, 255));

        let mut string = false;

        for (y, row) in self.buffer.lines().enumerate() {
            for (x, c) in row.chars().enumerate() {
                // TODO: Move outta here
                let color = if self.options.highlight {
                    match c {
                        '\'' | '"' => {
                            string = !string;
                            (226, 225, 167) //(167, 222, 156)
                        }
                        _ if string => (226, 225, 167), //(167, 222, 156)
                        '!' |
                        '@' |
                        '#' |
                        '$' |
                        '%' |
                        '^' |
                        '&' |
                        '|' |
                        '*' |
                        '+' |
                        '-' |
                        '/' |
                        ':' |
                        '=' |
                        '<' |
                        '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                        '.' | ',' => (241, 213, 226),
                        '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                        '0' ... '9' => (209, 209, 177),
                        _ => (255, 255, 255),
                    }
                } else {
                    (255, 255, 255)
                };

                let c = if c == '\t' {
                    ' '
                } else {
                    c
                };

                if pos_x == x && pos_y == y {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }

        self.redraw_task = RedrawTask::Null;

        self.redraw_status_bar();
        self.window.sync();
    }

    /// Redraw the status bar
    pub fn redraw_status_bar(&mut self) {
        let h = self.window.height();
        let w = self.window.width();
        let mode = self.cursor().mode;
        self.window.rect(0,
                         h as i32 - 18 -
                         {
                             if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                 18
                             } else {
                                 0
                             }
                         },
                         w,
                         18,
                         Color::rgba(74, 74, 74, 255));

        let sb_mode = self.status_bar.mode.clone();
        status_bar(self, sb_mode, 0, 4);
        let sb_file = self.status_bar.file.clone();
        status_bar(self, sb_file, 1, 4);
        let sb_cmd = self.status_bar.cmd.clone();
        status_bar(self, sb_cmd, 2, 4);
        let sb_msg = self.status_bar.msg.clone();
        status_bar(self, sb_msg, 3, 4);

        for (n, c) in self.prompt.chars().enumerate() {
            self.window.char(n as i32 * 8, h as i32 - 16 - 1, c, Color::rgb(255, 255, 255));
        }

        self.window.sync();
    }
}

#[cfg(feature = "orbital")]
fn status_bar(editor: &mut Editor, text: String, a: u32, b: u32) {

    let h = editor.window.height();
    let w = editor.window.width();
    // let y = editor.y();
    let mode = editor.cursor().mode;

    for (n, c) in (if text.len() as u32 > w / (8 * b) {
                      text.chars().take((w / (8 * b) - 5) as usize).chain(vec!['.'; 3]).collect::<Vec<_>>()
                  } else {
                      text.chars().collect()
                  })
                  .into_iter()
                  .enumerate() {

        editor.window.char(((w * a) / b) as i32 + (n as i32 * 8),
                           h as i32 - 16 - 1 -
                           {
                               if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                   16 + 1 + 1
                               } else {
                                   0
                               }
                           },
                           c,
                           Color::rgb(255, 255, 255));
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: "Welcome to Sodium!".to_string(),
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use ir;

pub fn emit_program<W: Write>(program: &ir::Program, mut out: W) -> io::Result<()> {
    for (name, func) in &program.functions {
        write!(out, "function {}", name.0)?;
        emit_function(func, &mut out)?;
        writeln!(out)?;
    }
    Ok(())
}

fn emit_function<W: Write>(func: &ir::Function, out: &mut W) -> io::Result<()> {
    write!(out, "(")?;
    let mut first = true;
    for param in &func.parameters {
        if !first {
            write!(out, ", ")?;
        }
        first = false;
        write!(out, "%{}", param.0)?;
    }
    write!(out, ")")?;
    if let Some(layout) = func.output_layout {
        writeln!(out, ": {{ {}, {} }}", layout.size, layout.align)?;
    } else {
        writeln!(out)?;
    }
    for (reg, layout) in &func.registers {
        writeln!(
            out,
            "    register %{}: size {}, align {}",
            reg.0,
            layout.size,
            layout.align
        )?;
    }
    if func.start_block.is_none() {
        return Ok(());
    }
    writeln!(out, "start:")?;
    writeln!(out, "    goto label_{}:", func.start_block.unwrap().0)?;
    for (id, block) in &func.blocks {
        writeln!(out, "label_{}:", id.0)?;
        for op in &block.ops {
            emit_instruction(op, out)?;
        }
        match block.end {
            ir::BlockEnd::Branch(ref val, a, b) => {
                write!(out, "    branch ")?;
                emit_value(val, out)?;
                writeln!(out, " label_{} label_{}", a.0, b.0)?;
            }
            ir::BlockEnd::Jump(id) => {
                writeln!(out, "    goto label_{}", id.0)?;
            }
            ir::BlockEnd::Return(ref val) => {
                write!(out, "    return ")?;
                emit_value(val, out)?;
                writeln!(out)?;
            }
            ir::BlockEnd::ReturnProc => {
                writeln!(out, "    return")?;
            }
        }
    }
    Ok(())
}

fn emit_instruction<W: Write>(i: &ir::Instruction, out: &mut W) -> io::Result<()> {
    match *i {
        ir::Instruction::Assign(reg, ref val) => {
            write!(out, "    %{} = ", reg.0)?;
            emit_value(val, out)?;
            writeln!(out)
        }
        ir::Instruction::CastAssign(reg, ref val) => {
            write!(out, "    %{} = cast ", reg.0)?;
            emit_value(val, out)?;
            writeln!(out)
        }
        ir::Instruction::BinaryOp(dest, op, ref a, ref b) => {
            write!(out, "    %{} = ", dest.0)?;
            emit_binop(op, out)?;
            write!(out, " ")?;
            emit_value(a, out)?;
            write!(out, " ")?;
            emit_value(b, out)?;
            writeln!(out)
        }
        ir::Instruction::Call(dest, ref sym, ref params) => {
            write!(out, "    %{} = call {}", dest.0, sym.0)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallProc(ref sym, ref params) => {
            write!(out, "    callproc {}", sym.0)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallVirt(dest, ref val, ref params) => {
            write!(out, "    %{} = callvirt ", dest.0)?;
            emit_value(val, out)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallProcVirt(ref val, ref params) => {
            write!(out, "    callprocvirt ")?;
            emit_value(val, out)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::DerefLoad(dest, ref value, offset) => {
            write!(out, "    %{} = deref (", dest.0)?;
            emit_value(value, out)?;
            writeln!(out, " + {})", offset)
        }
        ir::Instruction::DerefStore(ref address, offset, ref value) => {
            write!(out, "    store (")?;
            emit_value(address, out)?;
            write!(out, " + {}) ", offset)?;
            emit_value(value, out)?;
            writeln!(out)
        }
        ir::Instruction::Drop(reg) => writeln!(out, "    drop %{}", reg.0),
        ir::Instruction::Load(dest, reg, offset) => {
            writeln!(out, "    %{} = %{}[{}]", dest.0, reg.0, offset)
        }
        ir::Instruction::Store(dest, offset, ref value) => {
            write!(out, "    %{}[{}] = ", dest.0, offset)?;
            emit_value(value, out)?;
            writeln!(out)
        }
        ir::Instruction::TakeAddress(dest, of, offset) => {
            writeln!(out, "    %{} = address %{}[{}]", dest.0, of.0, offset)
        }
        ir::Instruction::UnaryOp(dest, ir::UnaryOp::Negate(sign, size), ref arg) => {
            write!(out, "    %{} = neg_", dest.0)?;
            emit_sign(sign, out)?;
            emit_size(size, out)?;
            write!(out, " ")?;
            emit_value(arg, out)?;
            writeln!(out)
        }
    }
}

fn emit_value<W: Write>(val: &ir::Value, out: &mut W) -> io::Result<()> {
    match *val {
        ir::Value::Bytes(ref bytes) => {
            write!(out, "\"")?;
            for &byte in bytes {
                if byte < 32 || byte > 126 {
                    write!(out, "\\x{:0>2x}", byte)?;
                } else if byte == b'"' {
                    write!(out, "\\\"")?;
                } else {
                    write!(out, "{}", byte as char)?;
                }
            }
            write!(out, "\"")
        }
        ir::Value::Int(value, size) => match size {
            ir::Size::Bit8 => write!(out, "{}_b8", value),
            ir::Size::Bit16 => write!(out, "{}_b16", value),
            ir::Size::Bit32 => write!(out, "{}_b32", value),
        },
        ir::Value::Reg(reg) => write!(out, "%{}", reg.0),
        ir::Value::Symbol(ref sym) => write!(out, "{}", sym.0),
    }
}

fn emit_binop<W: Write>(op: ir::BinaryOp, out: &mut W) -> io::Result<()> {
    match op {
        ir::BinaryOp::BitOp(ir::BitOp::And, s) => {
            write!(out, "and_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::BitOp(ir::BitOp::Or, s) => {
            write!(out, "or_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::BitOp(ir::BitOp::Xor, s) => {
            write!(out, "xor_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::Eq => write!(out, "eq"),
        ir::BinaryOp::Neq => write!(out, "neq"),
        ir::BinaryOp::IntOp(ir::IntOp::Add, sign, size) => {
            write!(out, "add_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Div, sign, size) => {
            write!(out, "div_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Greater, sign, size) => {
            write!(out, "gt_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::GreaterEq, sign, size) => {
            write!(out, "geq_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Less, sign, size) => {
            write!(out, "le_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::LessEq, sign, size) => {
            write!(out, "leq_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Mod, sign, size) => {
            write!(out, "mod_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Mul, sign, size) => {
            write!(out, "mul_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Sub, sign, size) => {
            write!(out, "sub_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
    }
}

fn emit_sign<W: Write>(sign: ir::Signedness, out: &mut W) -> io::Result<()> {
    match sign {
        ir::Signedness::Unsigned => write!(out, "u"),
        ir::Signedness::Signed => write!(out, "i"),
    }
}

fn emit_size<W: Write>(size: ir::Size, out: &mut W) -> io::Result<()> {
    match size {
        ir::Size::Bit8 => write!(out, "8"),
        ir::Size::Bit16 => write!(out, "16"),
        ir::Size::Bit32 => write!(out, "32"),
    }
}

fn emit_params<W: Write>(params: &[ir::Value], out: &mut W) -> io::Result<()> {
    write!(out, "(")?;
    let mut first = true;
    for param in params {
        if !first {
            write!(out, ", ")?;
        }
        first = false;
        emit_value(param, out)?;
    }
    write!(out, ")")
}
<commit_msg>remove trailing colon in start block<commit_after>use std::io::{self, Write};
use ir;

pub fn emit_program<W: Write>(program: &ir::Program, mut out: W) -> io::Result<()> {
    for (name, func) in &program.functions {
        write!(out, "function {}", name.0)?;
        emit_function(func, &mut out)?;
        writeln!(out)?;
    }
    Ok(())
}

fn emit_function<W: Write>(func: &ir::Function, out: &mut W) -> io::Result<()> {
    write!(out, "(")?;
    let mut first = true;
    for param in &func.parameters {
        if !first {
            write!(out, ", ")?;
        }
        first = false;
        write!(out, "%{}", param.0)?;
    }
    write!(out, ")")?;
    if let Some(layout) = func.output_layout {
        writeln!(out, ": {{ {}, {} }}", layout.size, layout.align)?;
    } else {
        writeln!(out)?;
    }
    for (reg, layout) in &func.registers {
        writeln!(
            out,
            "    register %{}: size {}, align {}",
            reg.0,
            layout.size,
            layout.align
        )?;
    }
    if func.start_block.is_none() {
        return Ok(());
    }
    writeln!(out, "start:")?;
    writeln!(out, "    goto label_{}", func.start_block.unwrap().0)?;
    for (id, block) in &func.blocks {
        writeln!(out, "label_{}:", id.0)?;
        for op in &block.ops {
            emit_instruction(op, out)?;
        }
        match block.end {
            ir::BlockEnd::Branch(ref val, a, b) => {
                write!(out, "    branch ")?;
                emit_value(val, out)?;
                writeln!(out, " label_{} label_{}", a.0, b.0)?;
            }
            ir::BlockEnd::Jump(id) => {
                writeln!(out, "    goto label_{}", id.0)?;
            }
            ir::BlockEnd::Return(ref val) => {
                write!(out, "    return ")?;
                emit_value(val, out)?;
                writeln!(out)?;
            }
            ir::BlockEnd::ReturnProc => {
                writeln!(out, "    return")?;
            }
        }
    }
    Ok(())
}

fn emit_instruction<W: Write>(i: &ir::Instruction, out: &mut W) -> io::Result<()> {
    match *i {
        ir::Instruction::Assign(reg, ref val) => {
            write!(out, "    %{} = ", reg.0)?;
            emit_value(val, out)?;
            writeln!(out)
        }
        ir::Instruction::CastAssign(reg, ref val) => {
            write!(out, "    %{} = cast ", reg.0)?;
            emit_value(val, out)?;
            writeln!(out)
        }
        ir::Instruction::BinaryOp(dest, op, ref a, ref b) => {
            write!(out, "    %{} = ", dest.0)?;
            emit_binop(op, out)?;
            write!(out, " ")?;
            emit_value(a, out)?;
            write!(out, " ")?;
            emit_value(b, out)?;
            writeln!(out)
        }
        ir::Instruction::Call(dest, ref sym, ref params) => {
            write!(out, "    %{} = call {}", dest.0, sym.0)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallProc(ref sym, ref params) => {
            write!(out, "    callproc {}", sym.0)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallVirt(dest, ref val, ref params) => {
            write!(out, "    %{} = callvirt ", dest.0)?;
            emit_value(val, out)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::CallProcVirt(ref val, ref params) => {
            write!(out, "    callprocvirt ")?;
            emit_value(val, out)?;
            emit_params(params, out)?;
            writeln!(out)
        }
        ir::Instruction::DerefLoad(dest, ref value, offset) => {
            write!(out, "    %{} = deref (", dest.0)?;
            emit_value(value, out)?;
            writeln!(out, " + {})", offset)
        }
        ir::Instruction::DerefStore(ref address, offset, ref value) => {
            write!(out, "    store (")?;
            emit_value(address, out)?;
            write!(out, " + {}) ", offset)?;
            emit_value(value, out)?;
            writeln!(out)
        }
        ir::Instruction::Drop(reg) => writeln!(out, "    drop %{}", reg.0),
        ir::Instruction::Load(dest, reg, offset) => {
            writeln!(out, "    %{} = %{}[{}]", dest.0, reg.0, offset)
        }
        ir::Instruction::Store(dest, offset, ref value) => {
            write!(out, "    %{}[{}] = ", dest.0, offset)?;
            emit_value(value, out)?;
            writeln!(out)
        }
        ir::Instruction::TakeAddress(dest, of, offset) => {
            writeln!(out, "    %{} = address %{}[{}]", dest.0, of.0, offset)
        }
        ir::Instruction::UnaryOp(dest, ir::UnaryOp::Negate(sign, size), ref arg) => {
            write!(out, "    %{} = neg_", dest.0)?;
            emit_sign(sign, out)?;
            emit_size(size, out)?;
            write!(out, " ")?;
            emit_value(arg, out)?;
            writeln!(out)
        }
    }
}

fn emit_value<W: Write>(val: &ir::Value, out: &mut W) -> io::Result<()> {
    match *val {
        ir::Value::Bytes(ref bytes) => {
            write!(out, "\"")?;
            for &byte in bytes {
                if byte < 32 || byte > 126 {
                    write!(out, "\\x{:0>2x}", byte)?;
                } else if byte == b'"' {
                    write!(out, "\\\"")?;
                } else {
                    write!(out, "{}", byte as char)?;
                }
            }
            write!(out, "\"")
        }
        ir::Value::Int(value, size) => match size {
            ir::Size::Bit8 => write!(out, "{}_b8", value),
            ir::Size::Bit16 => write!(out, "{}_b16", value),
            ir::Size::Bit32 => write!(out, "{}_b32", value),
        },
        ir::Value::Reg(reg) => write!(out, "%{}", reg.0),
        ir::Value::Symbol(ref sym) => write!(out, "{}", sym.0),
    }
}

fn emit_binop<W: Write>(op: ir::BinaryOp, out: &mut W) -> io::Result<()> {
    match op {
        ir::BinaryOp::BitOp(ir::BitOp::And, s) => {
            write!(out, "and_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::BitOp(ir::BitOp::Or, s) => {
            write!(out, "or_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::BitOp(ir::BitOp::Xor, s) => {
            write!(out, "xor_")?;
            emit_size(s, out)
        }
        ir::BinaryOp::Eq => write!(out, "eq"),
        ir::BinaryOp::Neq => write!(out, "neq"),
        ir::BinaryOp::IntOp(ir::IntOp::Add, sign, size) => {
            write!(out, "add_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Div, sign, size) => {
            write!(out, "div_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Greater, sign, size) => {
            write!(out, "gt_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::GreaterEq, sign, size) => {
            write!(out, "geq_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Less, sign, size) => {
            write!(out, "le_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::LessEq, sign, size) => {
            write!(out, "leq_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Mod, sign, size) => {
            write!(out, "mod_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Mul, sign, size) => {
            write!(out, "mul_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
        ir::BinaryOp::IntOp(ir::IntOp::Sub, sign, size) => {
            write!(out, "sub_")?;
            emit_sign(sign, out)?;
            emit_size(size, out)
        }
    }
}

fn emit_sign<W: Write>(sign: ir::Signedness, out: &mut W) -> io::Result<()> {
    match sign {
        ir::Signedness::Unsigned => write!(out, "u"),
        ir::Signedness::Signed => write!(out, "i"),
    }
}

fn emit_size<W: Write>(size: ir::Size, out: &mut W) -> io::Result<()> {
    match size {
        ir::Size::Bit8 => write!(out, "8"),
        ir::Size::Bit16 => write!(out, "16"),
        ir::Size::Bit32 => write!(out, "32"),
    }
}

fn emit_params<W: Write>(params: &[ir::Value], out: &mut W) -> io::Result<()> {
    write!(out, "(")?;
    let mut first = true;
    for param in params {
        if !first {
            write!(out, ", ")?;
        }
        first = false;
        emit_value(param, out)?;
    }
    write!(out, ")")
}
<|endoftext|>