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

#[macro_export]
macro_rules! log(
    ($lvl:expr, $($arg:tt)+) => ({
        let lvl = $lvl;
        if lvl <= __log_level() {
            format_args!(|args| {
                ::std::logging::log(lvl, args)
            }, $($arg)+)
        }
    })
)
#[macro_export]
macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
#[macro_export]
macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
#[macro_export]
macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
#[macro_export]
macro_rules! debug( ($($arg:tt)*) => (
    if cfg!(not(ndebug)) { log!(4u32, $($arg)*) }
))

#[macro_export]
macro_rules! log_enabled(
    ($lvl:expr) => ( {
        let lvl = $lvl;
        lvl <= __log_level() && (lvl != 4 || cfg!(not(ndebug)))
    } )
)

#[macro_export]
macro_rules! fail(
    () => (
        fail!("explicit failure")
    );
    ($msg:expr) => (
        ::std::rt::begin_unwind($msg, file!(), line!())
    );
    ($fmt:expr, $($arg:tt)*) => (
        {
            // a closure can't have return type !, so we need a full
            // function to pass to format_args!, *and* we need the
            // file and line numbers right here; so an inner bare fn
            // is our only choice.
            #[inline]
            fn run_fmt(fmt: &::std::fmt::Arguments) -> ! {
                ::std::rt::begin_unwind_fmt(fmt, file!(), line!())
            }
            format_args!(run_fmt, $fmt, $($arg)*)
        }
        )
)

#[macro_export]
macro_rules! assert(
    ($cond:expr) => {
        if !$cond {
            fail!("assertion failed: {:s}", stringify!($cond))
        }
    };
    ($cond:expr, $msg:expr) => {
        if !$cond {
            fail!($msg)
        }
    };
    ($cond:expr, $( $arg:expr ),+) => {
        if !$cond {
            fail!( $($arg),+ )
        }
    }
)

#[macro_export]
macro_rules! assert_eq (
    ($given:expr , $expected:expr) => (
        {
            let given_val = &($given);
            let expected_val = &($expected);
            // check both directions of equality....
            if !((*given_val == *expected_val) &&
                 (*expected_val == *given_val)) {
                fail!("assertion failed: `(left == right) && (right == left)` \
                       (left: `{:?}`, right: `{:?}`)", *given_val, *expected_val)
            }
        }
    )
)

/// A utility macro for indicating unreachable code. It will fail if
/// executed. This is occasionally useful to put after loops that never
/// terminate normally, but instead directly return from a function.
///
/// # Example
///
/// ```rust
/// fn choose_weighted_item(v: &[Item]) -> Item {
///     assert!(!v.is_empty());
///     let mut so_far = 0u;
///     for item in v.iter() {
///         so_far += item.weight;
///         if so_far > 100 {
///             return item;
///         }
///     }
///     // The above loop always returns, so we must hint to the
///     // type checker that it isn't possible to get down here
///     unreachable!();
/// }
/// ```
#[macro_export]
macro_rules! unreachable (() => (
    fail!("internal error: entered unreachable code");
))

/// A standardised placeholder for marking unfinished code. It fails with the
/// message `"not yet implemented"` when executed.
#[macro_export]
macro_rules! unimplemented(
    () => (fail!("not yet implemented"))
)

#[macro_export]
macro_rules! format(($($arg:tt)*) => (
    format_args!(::std::fmt::format, $($arg)*)
))
#[macro_export]
macro_rules! write(($dst:expr, $($arg:tt)*) => (
    format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*)
))
#[macro_export]
macro_rules! writeln(($dst:expr, $($arg:tt)*) => (
    format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
))
#[macro_export]
macro_rules! print (
    ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*))
)
#[macro_export]
macro_rules! println (
    ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*))
)

#[macro_export]
macro_rules! local_data_key (
    ($name:ident: $ty:ty) => (
        static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
    );
    (pub $name:ident: $ty:ty) => (
        pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
    )
)

#[macro_export]
macro_rules! if_ok (
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
)
<commit_msg>Clean up formatting in macros module<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[macro_escape];

#[macro_export]
macro_rules! log(
    ($lvl:expr, $($arg:tt)+) => ({
        let lvl = $lvl;
        if lvl <= __log_level() {
            format_args!(|args| {
                ::std::logging::log(lvl, args)
            }, $($arg)+)
        }
    })
)

#[macro_export]
macro_rules! error(
    ($($arg:tt)*) => (log!(1u32, $($arg)*))
)

#[macro_export]
macro_rules! warn(
    ($($arg:tt)*) => (log!(2u32, $($arg)*))
)

#[macro_export]
macro_rules! info(
    ($($arg:tt)*) => (log!(3u32, $($arg)*))
)

#[macro_export]
macro_rules! debug(
    ($($arg:tt)*) => (if cfg!(not(ndebug)) { log!(4u32, $($arg)*) })
)

#[macro_export]
macro_rules! log_enabled(
    ($lvl:expr) => ({
        let lvl = $lvl;
        lvl <= __log_level() && (lvl != 4 || cfg!(not(ndebug)))
    })
)

#[macro_export]
macro_rules! fail(
    () => (
        fail!("explicit failure")
    );
    ($msg:expr) => (
        ::std::rt::begin_unwind($msg, file!(), line!())
    );
    ($fmt:expr, $($arg:tt)*) => ({
        // a closure can't have return type !, so we need a full
        // function to pass to format_args!, *and* we need the
        // file and line numbers right here; so an inner bare fn
        // is our only choice.
        #[inline]
        fn run_fmt(fmt: &::std::fmt::Arguments) -> ! {
            ::std::rt::begin_unwind_fmt(fmt, file!(), line!())
        }
        format_args!(run_fmt, $fmt, $($arg)*)
    });
)

#[macro_export]
macro_rules! assert(
    ($cond:expr) => (
        if !$cond {
            fail!("assertion failed: {:s}", stringify!($cond))
        }
    );
    ($cond:expr, $msg:expr) => (
        if !$cond {
            fail!($msg)
        }
    );
    ($cond:expr, $($arg:expr),+) => (
        if !$cond {
            fail!($($arg),+)
        }
    );
)

#[macro_export]
macro_rules! assert_eq(
    ($given:expr , $expected:expr) => ({
        let given_val = &($given);
        let expected_val = &($expected);
        // check both directions of equality....
        if !((*given_val == *expected_val) &&
             (*expected_val == *given_val)) {
            fail!("assertion failed: `(left == right) && (right == left)` \
                   (left: `{:?}`, right: `{:?}`)", *given_val, *expected_val)
        }
    })
)

/// A utility macro for indicating unreachable code. It will fail if
/// executed. This is occasionally useful to put after loops that never
/// terminate normally, but instead directly return from a function.
///
/// # Example
///
/// ~~~rust
/// fn choose_weighted_item(v: &[Item]) -> Item {
///     assert!(!v.is_empty());
///     let mut so_far = 0u;
///     for item in v.iter() {
///         so_far += item.weight;
///         if so_far > 100 {
///             return item;
///         }
///     }
///     // The above loop always returns, so we must hint to the
///     // type checker that it isn't possible to get down here
///     unreachable!();
/// }
/// ~~~
#[macro_export]
macro_rules! unreachable(
    () => (fail!("internal error: entered unreachable code"))
)

/// A standardised placeholder for marking unfinished code. It fails with the
/// message `"not yet implemented"` when executed.
#[macro_export]
macro_rules! unimplemented(
    () => (fail!("not yet implemented"))
)

#[macro_export]
macro_rules! format(
    ($($arg:tt)*) => (
        format_args!(::std::fmt::format, $($arg)*)
    )
)

#[macro_export]
macro_rules! write(
    ($dst:expr, $($arg:tt)*) => (
        format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*)
    )
)

#[macro_export]
macro_rules! writeln(
    ($dst:expr, $($arg:tt)*) => (
        format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
    )
)

#[macro_export]
macro_rules! print(
    ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*))
)

#[macro_export]
macro_rules! println(
    ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*))
)

#[macro_export]
macro_rules! local_data_key(
    ($name:ident: $ty:ty) => (
        static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
    );
    (pub $name:ident: $ty:ty) => (
        pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
    );
)

#[macro_export]
macro_rules! if_ok(
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
)
<|endoftext|>


<commit_before>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore).expect("open gitignore");
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect("metadata").is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect("remove_file");
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    assert!(!protos.is_empty());

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &["../proto", include_dir],
        .. Default::default()
    }).expect("protoc");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<commit_msg>TODO about tests<commit_after>//! Common code of `build.rs` of two tests

pub use protobuf_codegen::Customize;

use std::io::Write;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::fs;
use std::fmt;
use std::path::Path;

use glob;

pub fn glob_simple(pattern: &str) -> Vec<String> {
    let mut r: Vec<_> = glob::glob(pattern)
        .expect("glob")
        .map(|g| {
            g.expect("item")
                .as_path()
                .to_str()
                .expect("utf-8")
                .to_owned()
        })
        .collect();
    // Make test stable
    r.sort();
    r
}

fn read_gitignore(dir: &Path) -> Vec<String> {
    let mut patterns = Vec::new();

    let gitignore = format!("{}/.gitignore", dir.display());
    let gitignore = &Path::new(&gitignore);
    if gitignore.exists() {
        let gitignore = fs::File::open(gitignore).expect("open gitignore");
        for line in BufReader::new(gitignore).lines() {
            let line = line.expect("read_line");
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            patterns.push(line);
        }
    }

    patterns
}

fn clean_recursively(dir: &Path, patterns: &[&str]) {
    assert!(dir.is_dir());

    let gitignore_patterns = read_gitignore(dir);

    let mut patterns = patterns.to_vec();
    patterns.extend(gitignore_patterns.iter().map(String::as_str));

    let patterns_compiled: Vec<_> = patterns.iter()
        .map(|&p| glob::Pattern::new(p).expect("failed to compile pattern"))
        .collect();

    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();
        if entry.metadata().expect("metadata").is_dir() {
            clean_recursively(&entry_path, &patterns);
        } else if file_name == ".gitignore" {
            // keep it
        } else {
            for pattern in &patterns_compiled {
                if pattern.matches(file_name) {
                    fs::remove_file(&entry_path).expect("remove_file");
                    break;
                }
            }
        }
    }
}

pub fn clean_old_files() {
    clean_recursively(&Path::new("src"), &["*_pb.rs", "*_pb_proto3.rs"]);
}

#[derive(Default)]
pub struct GenInDirArgs<'a> {
    pub out_dir: &'a str,
    pub input: &'a [&'a str],
    pub includes: &'a [&'a str],
    pub customize: Customize,
}

/// Generate mod.rs from all files in a directory
pub fn gen_mod_rs_in_dir(dir: &str) {
    assert!(Path::new(dir).is_dir());

    let mut mod_rs = fs::File::create(&format!("{}/mod.rs", dir)).expect("create");

    writeln!(mod_rs, "// generated by {}", module_path!()).expect("write");
    writeln!(mod_rs, "").expect("write");

    let rs_files = glob_simple(&format!("{}/*.rs", dir));

    for rs in rs_files {
        let file_name = Path::new(&rs).file_name().expect("file_name").to_str().expect("file_name");
        if file_name == "mod.rs" {
            continue;
        }
        assert!(file_name.ends_with(".rs"));
        let mod_name = &file_name[..file_name.len() - ".rs".len()];

        if mod_name.contains("carllerche") {
            writeln!(mod_rs, r#"#[cfg(feature = "with-bytes")]"#).expect("write");
        }
        writeln!(mod_rs, "mod {};", mod_name).expect("write");
    }

    mod_rs.flush().expect("flush");
}

pub fn gen_in_dir_impl<F, E>(dir: &str, include_dir: &str, gen: F)
    where
        F : for<'a> Fn(GenInDirArgs<'a>) -> Result<(), E>,
        E : fmt::Debug,
{
    info!("generating protos in {}", dir);

    // TODO: assert that all files in `vN` has `syntax = "protoN"`

    let mut protos = Vec::new();
    for suffix in &[".proto", ".proto3"] {
        protos.extend(glob_simple(&format!("{}/*{}", dir, suffix)));
    }

    assert!(!protos.is_empty());

    gen(GenInDirArgs {
        out_dir: dir,
        input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
        includes: &["../proto", include_dir],
        .. Default::default()
    }).expect("protoc");

    gen_mod_rs_in_dir(dir);
}

pub fn list_tests_in_dir(dir: &str) -> Vec<String> {
    let mut r = Vec::new();
    for entry in fs::read_dir(dir).expect("read_dir") {
        let entry = entry.expect("entry");
        let entry_path = entry.path();
        let file_name = entry_path.as_path().file_name().unwrap().to_str().unwrap();

        // temporart files
        if file_name.ends_with(".") {
            continue;
        }

        if !file_name.ends_with(".rs") || file_name.ends_with("_pb.rs") {
            continue;
        }

        if file_name == "mod.rs" {
            continue;
        }

        r.push(file_name[..file_name.len() - ".rs".len()].to_owned());
    }

    // Make test stable
    r.sort();

    r
}

pub fn copy_tests_v2_v3(v2_dir: &str, v3_dir: &str) {
    for test_name in list_tests_in_dir(v2_dir) {
        let mut p2f = fs::File::open(&format!("{}/{}_pb.proto", v2_dir, test_name))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("{}/{}.rs", v2_dir, test_name))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("{}/{}_pb.proto", v3_dir, test_name))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("{}/{}.rs", v3_dir, test_name))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");
    }
}
<|endoftext|>


<commit_before>use gfx;
use gfx::traits::*;

use utils::{grow_buffer, MAT4_ID};
use std::marker::PhantomData;

pub struct LineRenderer<R: gfx::Resources> {
    program: gfx::ProgramHandle<R>,
    state: gfx::DrawState,
    vertex_data: Vec<Vertex>,
    vertex_buffer: gfx::BufferHandle<R, Vertex>,
    params: LineShaderParams<R>,
}

impl<R: gfx::Resources> LineRenderer<R> {

    pub fn new<F: gfx::Factory<R>>(
        device_capabilities: gfx::device::Capabilities,
        factory: &mut F,
        initial_buffer_size: usize
    ) -> Result<LineRenderer<R>, gfx::ProgramError> {

        let shader_model = device_capabilities.shader_model;

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SRC[0]),
            glsl_150: Some(VERTEX_SRC[1]),
            .. gfx::ShaderSource::empty()
        };

        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SRC[0]),
            glsl_150: Some(FRAGMENT_SRC[1]),
            .. gfx::ShaderSource::empty()
        };

        let program = match factory.link_program(
            vertex.choose(shader_model).unwrap(),
            fragment.choose(shader_model).unwrap()
        ) {
            Ok(program_handle) => program_handle,
            Err(e) => return Err(e),
        };

        let vertex_buffer = factory.create_buffer::<Vertex>(initial_buffer_size, gfx::BufferUsage::Dynamic);

        Ok(LineRenderer {
            vertex_data: Vec::new(),
            program: program,
            state: gfx::DrawState::new(),
            vertex_buffer: vertex_buffer,
            params: LineShaderParams {
                u_model_view_proj: MAT4_ID,
                _marker: PhantomData,
            },
        })
    }

    ///
    /// Add a line to the batch to be drawn on 'render'
    ///
    pub fn draw_line(&mut self, start: [f32; 3], end: [f32; 3], color: [f32; 4]) {
        self.vertex_data.push(Vertex{position: start, color: color});
        self.vertex_data.push(Vertex{position: end, color: color});
    }

    ///
    /// Draw and clear the current batch of lines
    ///
    pub fn render<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
    > (
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        factory: &mut F,
        output: &O,
        projection: [[f32; 4]; 4],
    ) {

        if self.vertex_data.len() > self.vertex_buffer.len() {
            self.vertex_buffer = gfx::BufferHandle::from_raw(grow_buffer::<R, F, Vertex>(factory, &self.vertex_buffer.raw(), self.vertex_data.len()));
        }

        factory.update_buffer(&self.vertex_buffer, &self.vertex_data[..], 0);

        self.params.u_model_view_proj = projection;

        let mesh = gfx::Mesh::from_format(
            self.vertex_buffer.clone(),
            self.vertex_data.len() as gfx::VertexCount
        );

        let slice = mesh.to_slice(gfx::PrimitiveType::Line);

        renderer.draw(
            &gfx::batch::bind(&self.state, &mesh, slice, &self.program, &self.params),
            output
        ).unwrap();

        self.vertex_data.clear();
    }
}

static VERTEX_SRC: [&'static [u8]; 2] = [
b"
    #version 120

    uniform mat4 u_model_view_proj;
    attribute vec3 position;
    attribute vec4 color;
    varying vec4 v_color;

    void main() {
        gl_Position = u_model_view_proj * vec4(position, 1.0);
        v_color = color;
    }
",
b"
    #version 150 core

    uniform mat4 u_model_view_proj;
    in vec3 position;
    in vec4 color;
    out vec4 v_color;

    void main() {
        gl_Position = u_model_view_proj * vec4(position, 1.0);
        v_color = color;
    }
"];

static FRAGMENT_SRC: [&'static [u8]; 2] = [
b"
    #version 120

    varying vec4 v_color;

    void main() {
        gl_FragColor = v_color;
    }
",
b"
    #version 150

    in vec4 v_color;
    out vec4 out_color;

    void main() {
        out_color = v_color;
    }
"];

#[vertex_format]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
struct Vertex {
    position: [f32; 3],
    color: [f32; 4],
}

#[shader_param]
struct LineShaderParams<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    _marker: PhantomData<R>,
}
<commit_msg>Using link_program_source<commit_after>use gfx;
use gfx::traits::*;

use utils::{grow_buffer, MAT4_ID};
use std::marker::PhantomData;

pub struct LineRenderer<R: gfx::Resources> {
    program: gfx::ProgramHandle<R>,
    state: gfx::DrawState,
    vertex_data: Vec<Vertex>,
    vertex_buffer: gfx::BufferHandle<R, Vertex>,
    params: LineShaderParams<R>,
}

impl<R: gfx::Resources> LineRenderer<R> {

    pub fn new<F: gfx::Factory<R>>(
        device_capabilities: gfx::device::Capabilities,
        factory: &mut F,
        initial_buffer_size: usize
    ) -> Result<LineRenderer<R>, gfx::ProgramError> {

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SRC[0]),
            glsl_150: Some(VERTEX_SRC[1]),
            .. gfx::ShaderSource::empty()
        };

        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SRC[0]),
            glsl_150: Some(FRAGMENT_SRC[1]),
            .. gfx::ShaderSource::empty()
        };

        let program = match factory.link_program_source(
            vertex, fragment, &device_capabilities
        ){
            Ok(program_handle) => program_handle,
            Err(e) => return Err(e),
        };

        let vertex_buffer = factory.create_buffer::<Vertex>(initial_buffer_size, gfx::BufferUsage::Dynamic);

        Ok(LineRenderer {
            vertex_data: Vec::new(),
            program: program,
            state: gfx::DrawState::new(),
            vertex_buffer: vertex_buffer,
            params: LineShaderParams {
                u_model_view_proj: MAT4_ID,
                _marker: PhantomData,
            },
        })
    }

    ///
    /// Add a line to the batch to be drawn on 'render'
    ///
    pub fn draw_line(&mut self, start: [f32; 3], end: [f32; 3], color: [f32; 4]) {
        self.vertex_data.push(Vertex{position: start, color: color});
        self.vertex_data.push(Vertex{position: end, color: color});
    }

    ///
    /// Draw and clear the current batch of lines
    ///
    pub fn render<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
    > (
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        factory: &mut F,
        output: &O,
        projection: [[f32; 4]; 4],
    ) {

        if self.vertex_data.len() > self.vertex_buffer.len() {
            self.vertex_buffer = gfx::BufferHandle::from_raw(grow_buffer::<R, F, Vertex>(factory, &self.vertex_buffer.raw(), self.vertex_data.len()));
        }

        factory.update_buffer(&self.vertex_buffer, &self.vertex_data[..], 0);

        self.params.u_model_view_proj = projection;

        let mesh = gfx::Mesh::from_format(
            self.vertex_buffer.clone(),
            self.vertex_data.len() as gfx::VertexCount
        );

        let slice = mesh.to_slice(gfx::PrimitiveType::Line);

        renderer.draw(
            &gfx::batch::bind(&self.state, &mesh, slice, &self.program, &self.params),
            output
        ).unwrap();

        self.vertex_data.clear();
    }
}

static VERTEX_SRC: [&'static [u8]; 2] = [
b"
    #version 120

    uniform mat4 u_model_view_proj;
    attribute vec3 position;
    attribute vec4 color;
    varying vec4 v_color;

    void main() {
        gl_Position = u_model_view_proj * vec4(position, 1.0);
        v_color = color;
    }
",
b"
    #version 150 core

    uniform mat4 u_model_view_proj;
    in vec3 position;
    in vec4 color;
    out vec4 v_color;

    void main() {
        gl_Position = u_model_view_proj * vec4(position, 1.0);
        v_color = color;
    }
"];

static FRAGMENT_SRC: [&'static [u8]; 2] = [
b"
    #version 120

    varying vec4 v_color;

    void main() {
        gl_FragColor = v_color;
    }
",
b"
    #version 150

    in vec4 v_color;
    out vec4 out_color;

    void main() {
        out_color = v_color;
    }
"];

#[vertex_format]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
struct Vertex {
    position: [f32; 3],
    color: [f32; 4],
}

#[shader_param]
struct LineShaderParams<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    _marker: PhantomData<R>,
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::{env, fs, process};

use object::read::elf::{FileHeader, Rel, Rela, SectionHeader};
use object::Endianness;
use object::{elf, pe, SectionIndex};

fn main() {
    let mut args = env::args();
    if args.len() != 3 {
        eprintln!("Usage: {} <infile> <outfile>", args.next().unwrap());
        process::exit(1);
    }

    args.next();
    let in_file_path = args.next().unwrap();
    let out_file_path = args.next().unwrap();

    let in_file = match fs::File::open(&in_file_path) {
        Ok(file) => file,
        Err(err) => {
            eprintln!("Failed to open file '{}': {}", in_file_path, err,);
            process::exit(1);
        }
    };
    let in_data = match unsafe { memmap2::Mmap::map(&in_file) } {
        Ok(mmap) => mmap,
        Err(err) => {
            eprintln!("Failed to map file '{}': {}", in_file_path, err,);
            process::exit(1);
        }
    };
    let in_data = &*in_data;

    let kind = match object::FileKind::parse(in_data) {
        Ok(file) => file,
        Err(err) => {
            eprintln!("Failed to parse file: {}", err);
            process::exit(1);
        }
    };
    let out_data = match kind {
        object::FileKind::Elf32 => copy_file::<elf::FileHeader32<Endianness>>(in_data).unwrap(),
        object::FileKind::Elf64 => copy_file::<elf::FileHeader64<Endianness>>(in_data).unwrap(),
        _ => {
            eprintln!("Not an ELF file");
            process::exit(1);
        }
    };
    if let Err(err) = fs::write(&out_file_path, out_data) {
        eprintln!("Failed to write file '{}': {}", out_file_path, err);
        process::exit(1);
    }
}

fn copy_file<Elf: FileHeader<Endian = Endianness>>(
    in_data: &[u8],
) -> Result<Vec<u8>, Box<dyn Error>> {
    let in_elf = Elf::parse(in_data)?;
    let endian = in_elf.endian()?;
    let is_mips64el = in_elf.is_mips64el(endian);
    let in_sections = in_elf.sections(endian, in_data)?;

    let text = in_sections.iter().find(|s| is_text(*s, endian)).unwrap();
    let alignment = text.sh_addralign(endian).into();

    // Calculate text and data layout.
    // For now, we use the ELF layout without any change, and require
    // sections to be in order of address, and all text sections must
    // appear before data sections.
    let mut text_start = !0;
    let mut text_end = 0;
    let mut have_data = false;
    let mut data_start = !0;
    let mut data_end = 0;
    for in_section in in_sections.iter() {
        if !is_alloc(in_section, endian) {
            continue;
        }
        assert_eq!(in_section.sh_addralign(endian).into(), alignment);
        let start = in_section.sh_addr(endian).into() as u32;
        let end = start + in_section.sh_size(endian).into() as u32;
        if is_text(in_section, endian) {
            assert!(text_end <= start);
            if text_start > start {
                text_start = start;
            }
            if text_end < end {
                text_end = end;
            }
        } else if is_data(in_section, endian) {
            assert!(data_end <= start);
            have_data = true;
            if data_start > start {
                data_start = start;
            }
            if data_end < end {
                data_end = end;
            }
        } else {
            unreachable!();
        }
    }
    assert!(text_start <= text_end);
    if have_data {
        assert!(text_end <= data_start);
        assert!(data_start <= data_end);
    }

    let machine = match in_elf.e_machine(endian) {
        elf::EM_ARM => pe::IMAGE_FILE_MACHINE_THUMB,
        elf::EM_AARCH64 => pe::IMAGE_FILE_MACHINE_ARM64,
        elf::EM_RISCV => {
            if in_elf.is_class_64() {
                pe::IMAGE_FILE_MACHINE_RISCV64
            } else {
                pe::IMAGE_FILE_MACHINE_RISCV32
            }
        }
        _ => unimplemented!(),
    };

    let mut out_data = Vec::new();
    let mut writer = object::write::pe::Writer::new(
        in_elf.is_type_64(),
        alignment as u32,
        alignment as u32,
        &mut out_data,
    );

    // Add relocations
    for in_section in in_sections.iter() {
        if let Some((rels, _)) = in_section.rel(endian, in_data)? {
            let info_index = SectionIndex(in_section.sh_info(endian) as usize);
            let info = in_sections.section(info_index).unwrap();
            if !is_alloc(info, endian) {
                continue;
            }
            for rel in rels {
                let r_offset = rel.r_offset(endian).into() as u32;
                let r_type = rel.r_type(endian);
                match machine {
                    pe::IMAGE_FILE_MACHINE_THUMB => {
                        match r_type {
                            elf::R_ARM_PC24
                            | elf::R_ARM_REL32
                            | elf::R_ARM_THM_PC22
                            | elf::R_ARM_CALL
                            | elf::R_ARM_JUMP24
                            | elf::R_ARM_THM_JUMP24 => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_ARM_ABS32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    _ => unimplemented!(),
                }
            }
        } else if let Some((relas, _)) = in_section.rela(endian, in_data)? {
            let info_index = SectionIndex(in_section.sh_info(endian) as usize);
            let info = in_sections.section(info_index).unwrap();
            if !is_alloc(info, endian) {
                continue;
            }
            for rela in relas {
                let r_offset = rela.r_offset(endian).into() as u32;
                let r_type = rela.r_type(endian, is_mips64el);
                match machine {
                    pe::IMAGE_FILE_MACHINE_ARM64 => {
                        match r_type {
                            elf::R_AARCH64_PREL64
                            | elf::R_AARCH64_PREL32
                            | elf::R_AARCH64_PREL16
                            | elf::R_AARCH64_LD_PREL_LO19
                            | elf::R_AARCH64_ADR_PREL_LO21
                            | elf::R_AARCH64_ADR_PREL_PG_HI21
                            | elf::R_AARCH64_CONDBR19
                            | elf::R_AARCH64_JUMP26
                            | elf::R_AARCH64_CALL26 => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_AARCH64_ADD_ABS_LO12_NC
                            | elf::R_AARCH64_LDST8_ABS_LO12_NC
                            | elf::R_AARCH64_LDST16_ABS_LO12_NC
                            | elf::R_AARCH64_LDST32_ABS_LO12_NC
                            | elf::R_AARCH64_LDST64_ABS_LO12_NC
                            | elf::R_AARCH64_LDST128_ABS_LO12_NC => {
                                // ABS_LO12 relocations can be ignored if sections are aligned
                                // to pages.
                                assert!(alignment >= 0x1000);
                            }
                            elf::R_AARCH64_ABS64 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_DIR64);
                            }
                            elf::R_AARCH64_ABS32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    pe::IMAGE_FILE_MACHINE_RISCV64 => {
                        match r_type {
                            elf::R_RISCV_BRANCH
                            | elf::R_RISCV_JAL
                            | elf::R_RISCV_CALL
                            | elf::R_RISCV_RVC_BRANCH
                            | elf::R_RISCV_RVC_JUMP
                            | elf::R_RISCV_PCREL_HI20
                            | elf::R_RISCV_PCREL_LO12_I => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_RISCV_64 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_DIR64);
                            }
                            elf::R_RISCV_32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    _ => unimplemented!(),
                }
            }
        }
    }

    let mut section_num = 1;
    if have_data {
        section_num += 1;
    }
    if writer.has_relocs() {
        section_num += 1;
    }

    // Reserve file ranges and virtual addresses.
    writer.reserve_dos_header();
    writer.reserve_nt_headers(16);
    writer.reserve_section_headers(section_num);
    writer.reserve_virtual_until(text_start);
    let text_range = writer.reserve_text_section(text_end - text_start);
    assert_eq!(text_range.virtual_address, text_start);
    let mut data_range = Default::default();
    if have_data {
        writer.reserve_virtual_until(data_start);
        // TODO: handle bss
        data_range = writer.reserve_data_section(data_end - data_start, data_end - data_start);
        assert_eq!(data_range.virtual_address, data_start);
    }
    if writer.has_relocs() {
        writer.reserve_reloc_section();
    }

    // Start writing.
    writer.write_empty_dos_header()?;
    writer.write_nt_headers(object::write::pe::NtHeaders {
        machine,
        time_date_stamp: 0,
        characteristics: if in_elf.is_class_64() {
            pe::IMAGE_FILE_EXECUTABLE_IMAGE
                | pe::IMAGE_FILE_LINE_NUMS_STRIPPED
                | pe::IMAGE_FILE_LOCAL_SYMS_STRIPPED
                | pe::IMAGE_FILE_LARGE_ADDRESS_AWARE
        } else {
            pe::IMAGE_FILE_EXECUTABLE_IMAGE
                | pe::IMAGE_FILE_LINE_NUMS_STRIPPED
                | pe::IMAGE_FILE_LOCAL_SYMS_STRIPPED
                | pe::IMAGE_FILE_32BIT_MACHINE
        },
        major_linker_version: 0,
        minor_linker_version: 0,
        address_of_entry_point: in_elf.e_entry(endian).into() as u32,
        image_base: 0,
        major_operating_system_version: 0,
        minor_operating_system_version: 0,
        major_image_version: 0,
        minor_image_version: 0,
        major_subsystem_version: 0,
        minor_subsystem_version: 0,
        subsystem: pe::IMAGE_SUBSYSTEM_EFI_APPLICATION,
        dll_characteristics: 0,
        size_of_stack_reserve: 0,
        size_of_stack_commit: 0,
        size_of_heap_reserve: 0,
        size_of_heap_commit: 0,
    });
    writer.write_section_headers();

    writer.pad_until(text_range.file_offset);
    for in_section in in_sections.iter() {
        if !is_text(in_section, endian) {
            continue;
        }
        let offset = (in_section.sh_addr(endian).into() as u32)
            .checked_sub(text_range.virtual_address)
            .unwrap();
        writer.pad_until(text_range.file_offset + offset);
        writer.write(in_section.data(endian, in_data)?);
    }
    writer.pad_until(text_range.file_offset + text_range.file_size);
    if have_data {
        for in_section in in_sections.iter() {
            if !is_data(in_section, endian) {
                continue;
            }
            let offset = (in_section.sh_addr(endian).into() as u32)
                .checked_sub(data_range.virtual_address)
                .unwrap();
            writer.pad_until(data_range.file_offset + offset);
            writer.write(in_section.data(endian, in_data)?);
        }
        writer.pad_until(data_range.file_offset + data_range.file_size);
    }
    writer.write_reloc_section();

    debug_assert_eq!(writer.reserved_len() as usize, writer.len());

    Ok(out_data)
}

// Include both code and read only data in the text section.
fn is_text<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0 && (flags & elf::SHF_EXECINSTR != 0 || flags & elf::SHF_WRITE == 0)
}

// Anything that is alloc but not text.
fn is_data<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0 && flags & elf::SHF_EXECINSTR == 0 && flags & elf::SHF_WRITE != 0
}

fn is_alloc<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0
}
<commit_msg>elftoefi: relax alignment requirements<commit_after>use std::error::Error;
use std::{env, fs, process};

use object::read::elf::{FileHeader, Rel, Rela, SectionHeader};
use object::Endianness;
use object::{elf, pe, SectionIndex};

fn main() {
    let mut args = env::args();
    if args.len() != 3 {
        eprintln!("Usage: {} <infile> <outfile>", args.next().unwrap());
        process::exit(1);
    }

    args.next();
    let in_file_path = args.next().unwrap();
    let out_file_path = args.next().unwrap();

    let in_file = match fs::File::open(&in_file_path) {
        Ok(file) => file,
        Err(err) => {
            eprintln!("Failed to open file '{}': {}", in_file_path, err,);
            process::exit(1);
        }
    };
    let in_data = match unsafe { memmap2::Mmap::map(&in_file) } {
        Ok(mmap) => mmap,
        Err(err) => {
            eprintln!("Failed to map file '{}': {}", in_file_path, err,);
            process::exit(1);
        }
    };
    let in_data = &*in_data;

    let kind = match object::FileKind::parse(in_data) {
        Ok(file) => file,
        Err(err) => {
            eprintln!("Failed to parse file: {}", err);
            process::exit(1);
        }
    };
    let out_data = match kind {
        object::FileKind::Elf32 => copy_file::<elf::FileHeader32<Endianness>>(in_data).unwrap(),
        object::FileKind::Elf64 => copy_file::<elf::FileHeader64<Endianness>>(in_data).unwrap(),
        _ => {
            eprintln!("Not an ELF file");
            process::exit(1);
        }
    };
    if let Err(err) = fs::write(&out_file_path, out_data) {
        eprintln!("Failed to write file '{}': {}", out_file_path, err);
        process::exit(1);
    }
}

fn copy_file<Elf: FileHeader<Endian = Endianness>>(
    in_data: &[u8],
) -> Result<Vec<u8>, Box<dyn Error>> {
    let in_elf = Elf::parse(in_data)?;
    let endian = in_elf.endian()?;
    let is_mips64el = in_elf.is_mips64el(endian);
    let in_sections = in_elf.sections(endian, in_data)?;

    let text = in_sections.iter().find(|s| is_text(*s, endian)).unwrap();
    let alignment = text.sh_addralign(endian).into();

    // Calculate text and data layout.
    // For now, we use the ELF layout without any change, and require
    // sections to be in order of address, and all text sections must
    // appear before data sections.
    let mut text_start = !0;
    let mut text_end = 0;
    let mut have_data = false;
    let mut data_start = !0;
    let mut data_end = 0;
    for in_section in in_sections.iter() {
        if !is_alloc(in_section, endian) {
            continue;
        }
        assert!(in_section.sh_addralign(endian).into() <= alignment);
        let start = in_section.sh_addr(endian).into() as u32;
        let end = start + in_section.sh_size(endian).into() as u32;
        if is_text(in_section, endian) {
            assert!(text_end <= start);
            if text_start > start {
                text_start = start;
            }
            if text_end < end {
                text_end = end;
            }
        } else if is_data(in_section, endian) {
            assert!(data_end <= start);
            have_data = true;
            if data_start > start {
                data_start = start;
            }
            if data_end < end {
                data_end = end;
            }
        } else {
            unreachable!();
        }
    }
    assert!(text_start <= text_end);
    if have_data {
        assert!(text_end <= data_start);
        assert!(data_start <= data_end);
    }

    let machine = match in_elf.e_machine(endian) {
        elf::EM_ARM => pe::IMAGE_FILE_MACHINE_THUMB,
        elf::EM_AARCH64 => pe::IMAGE_FILE_MACHINE_ARM64,
        elf::EM_RISCV => {
            if in_elf.is_class_64() {
                pe::IMAGE_FILE_MACHINE_RISCV64
            } else {
                pe::IMAGE_FILE_MACHINE_RISCV32
            }
        }
        _ => unimplemented!(),
    };

    let mut out_data = Vec::new();
    let mut writer = object::write::pe::Writer::new(
        in_elf.is_type_64(),
        alignment as u32,
        alignment as u32,
        &mut out_data,
    );

    // Add relocations
    for in_section in in_sections.iter() {
        if let Some((rels, _)) = in_section.rel(endian, in_data)? {
            let info_index = SectionIndex(in_section.sh_info(endian) as usize);
            let info = in_sections.section(info_index).unwrap();
            if !is_alloc(info, endian) {
                continue;
            }
            for rel in rels {
                let r_offset = rel.r_offset(endian).into() as u32;
                let r_type = rel.r_type(endian);
                match machine {
                    pe::IMAGE_FILE_MACHINE_THUMB => {
                        match r_type {
                            elf::R_ARM_PC24
                            | elf::R_ARM_REL32
                            | elf::R_ARM_THM_PC22
                            | elf::R_ARM_CALL
                            | elf::R_ARM_JUMP24
                            | elf::R_ARM_THM_JUMP24 => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_ARM_ABS32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    _ => unimplemented!(),
                }
            }
        } else if let Some((relas, _)) = in_section.rela(endian, in_data)? {
            let info_index = SectionIndex(in_section.sh_info(endian) as usize);
            let info = in_sections.section(info_index).unwrap();
            if !is_alloc(info, endian) {
                continue;
            }
            for rela in relas {
                let r_offset = rela.r_offset(endian).into() as u32;
                let r_type = rela.r_type(endian, is_mips64el);
                match machine {
                    pe::IMAGE_FILE_MACHINE_ARM64 => {
                        match r_type {
                            elf::R_AARCH64_PREL64
                            | elf::R_AARCH64_PREL32
                            | elf::R_AARCH64_PREL16
                            | elf::R_AARCH64_LD_PREL_LO19
                            | elf::R_AARCH64_ADR_PREL_LO21
                            | elf::R_AARCH64_ADR_PREL_PG_HI21
                            | elf::R_AARCH64_CONDBR19
                            | elf::R_AARCH64_JUMP26
                            | elf::R_AARCH64_CALL26 => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_AARCH64_ADD_ABS_LO12_NC
                            | elf::R_AARCH64_LDST8_ABS_LO12_NC
                            | elf::R_AARCH64_LDST16_ABS_LO12_NC
                            | elf::R_AARCH64_LDST32_ABS_LO12_NC
                            | elf::R_AARCH64_LDST64_ABS_LO12_NC
                            | elf::R_AARCH64_LDST128_ABS_LO12_NC => {
                                // ABS_LO12 relocations can be ignored if sections are aligned
                                // to pages.
                                assert!(alignment >= 0x1000);
                            }
                            elf::R_AARCH64_ABS64 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_DIR64);
                            }
                            elf::R_AARCH64_ABS32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    pe::IMAGE_FILE_MACHINE_RISCV64 => {
                        match r_type {
                            elf::R_RISCV_BRANCH
                            | elf::R_RISCV_JAL
                            | elf::R_RISCV_CALL
                            | elf::R_RISCV_RVC_BRANCH
                            | elf::R_RISCV_RVC_JUMP
                            | elf::R_RISCV_PCREL_HI20
                            | elf::R_RISCV_PCREL_LO12_I => {
                                // Relative relocations can be ignored if relative offsets
                                // between sections are preserved.
                            }
                            elf::R_RISCV_64 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_DIR64);
                            }
                            elf::R_RISCV_32 => {
                                writer.add_reloc(r_offset, pe::IMAGE_REL_BASED_HIGHLOW);
                            }
                            _ => {
                                unimplemented!("relocation offset {:x}, type {}", r_offset, r_type);
                            }
                        }
                    }
                    _ => unimplemented!(),
                }
            }
        }
    }

    let mut section_num = 1;
    if have_data {
        section_num += 1;
    }
    if writer.has_relocs() {
        section_num += 1;
    }

    // Reserve file ranges and virtual addresses.
    writer.reserve_dos_header();
    writer.reserve_nt_headers(16);
    writer.reserve_section_headers(section_num);
    writer.reserve_virtual_until(text_start);
    let text_range = writer.reserve_text_section(text_end - text_start);
    assert_eq!(text_range.virtual_address, text_start);
    let mut data_range = Default::default();
    if have_data {
        writer.reserve_virtual_until(data_start);
        // TODO: handle bss
        data_range = writer.reserve_data_section(data_end - data_start, data_end - data_start);
        assert_eq!(data_range.virtual_address, data_start);
    }
    if writer.has_relocs() {
        writer.reserve_reloc_section();
    }

    // Start writing.
    writer.write_empty_dos_header()?;
    writer.write_nt_headers(object::write::pe::NtHeaders {
        machine,
        time_date_stamp: 0,
        characteristics: if in_elf.is_class_64() {
            pe::IMAGE_FILE_EXECUTABLE_IMAGE
                | pe::IMAGE_FILE_LINE_NUMS_STRIPPED
                | pe::IMAGE_FILE_LOCAL_SYMS_STRIPPED
                | pe::IMAGE_FILE_LARGE_ADDRESS_AWARE
        } else {
            pe::IMAGE_FILE_EXECUTABLE_IMAGE
                | pe::IMAGE_FILE_LINE_NUMS_STRIPPED
                | pe::IMAGE_FILE_LOCAL_SYMS_STRIPPED
                | pe::IMAGE_FILE_32BIT_MACHINE
        },
        major_linker_version: 0,
        minor_linker_version: 0,
        address_of_entry_point: in_elf.e_entry(endian).into() as u32,
        image_base: 0,
        major_operating_system_version: 0,
        minor_operating_system_version: 0,
        major_image_version: 0,
        minor_image_version: 0,
        major_subsystem_version: 0,
        minor_subsystem_version: 0,
        subsystem: pe::IMAGE_SUBSYSTEM_EFI_APPLICATION,
        dll_characteristics: 0,
        size_of_stack_reserve: 0,
        size_of_stack_commit: 0,
        size_of_heap_reserve: 0,
        size_of_heap_commit: 0,
    });
    writer.write_section_headers();

    writer.pad_until(text_range.file_offset);
    for in_section in in_sections.iter() {
        if !is_text(in_section, endian) {
            continue;
        }
        let offset = (in_section.sh_addr(endian).into() as u32)
            .checked_sub(text_range.virtual_address)
            .unwrap();
        writer.pad_until(text_range.file_offset + offset);
        writer.write(in_section.data(endian, in_data)?);
    }
    writer.pad_until(text_range.file_offset + text_range.file_size);
    if have_data {
        for in_section in in_sections.iter() {
            if !is_data(in_section, endian) {
                continue;
            }
            let offset = (in_section.sh_addr(endian).into() as u32)
                .checked_sub(data_range.virtual_address)
                .unwrap();
            writer.pad_until(data_range.file_offset + offset);
            writer.write(in_section.data(endian, in_data)?);
        }
        writer.pad_until(data_range.file_offset + data_range.file_size);
    }
    writer.write_reloc_section();

    debug_assert_eq!(writer.reserved_len() as usize, writer.len());

    Ok(out_data)
}

// Include both code and read only data in the text section.
fn is_text<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0 && (flags & elf::SHF_EXECINSTR != 0 || flags & elf::SHF_WRITE == 0)
}

// Anything that is alloc but not text.
fn is_data<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0 && flags & elf::SHF_EXECINSTR == 0 && flags & elf::SHF_WRITE != 0
}

fn is_alloc<S: SectionHeader>(s: &S, endian: S::Endian) -> bool {
    let flags = s.sh_flags(endian).into() as u32;
    flags & elf::SHF_ALLOC != 0
}
<|endoftext|>


<commit_before>use term_input::Key;
use termbox_simple::Termbox;

use std::convert::From;

use time::{self, Tm};

use crate::config::Colors;
use crate::exit_dialogue::ExitDialogue;
use crate::input_area::InputArea;
use crate::msg_area::line::SegStyle;
use crate::msg_area::{Layout, MsgArea};
use crate::trie::Trie;
use crate::widget::WidgetRet;

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub(crate) struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area: MsgArea,

    // exit_dialogue handles input when available.
    // two fields (instead of an enum etc.) to avoid borrowchk problems
    input_field: InputArea,
    exit_dialogue: Option<ExitDialogue>,

    width: i32,
    height: i32,

    // Option to disable status messages ( join/part )
    show_status: bool,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    nicks: Trie,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub(crate) struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    /// The width of the timestamp plus a space
    pub(crate) const WIDTH: usize = 6;
    fn stamp(self, msg_area: &mut MsgArea) {
        msg_area.add_text(
            &format!("{:02}:{:02} ", self.hour, self.min),
            SegStyle::Timestamp,
        );
    }

    /// Inserts a blank space that is the size of a timestamp
    fn blank(msg_area: &mut MsgArea) {
        msg_area.add_text(
            &format!("{:^width$}", ' ', width = Timestamp::WIDTH),
            SegStyle::Timestamp,
        );
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp {
            hour: tm.tm_hour,
            min: tm.tm_min,
        }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub(crate) fn new(
        width: i32,
        height: i32,
        status: bool,
        scrollback: usize,
        msg_layout: Layout,
    ) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1, scrollback, msg_layout),
            input_field: InputArea::new(width, get_input_field_max_height(height)),
            exit_dialogue: None,
            width,
            height,
            show_status: status,
            nicks: Trie::new(),
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub(crate) fn set_nick(&mut self, nick: String) {
        let nick_color = self.get_nick_color(&nick);
        self.input_field.set_nick(nick, nick_color);
        // update text field size
        let w = self.width;
        let h = self.height;
        self.resize(w, h);
    }

    pub(crate) fn get_nick(&self) -> Option<String> {
        self.input_field.get_nick()
    }

    pub(crate) fn draw(&mut self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        match &self.exit_dialogue {
            Some(exit_dialogue) => {
                exit_dialogue.draw(tb, colors, pos_x, self.height - 1);
            }
            None => {
                // Draw InputArea first because it can trigger a resize of MsgArea
                self.input_field
                    .draw(tb, colors, pos_x, pos_y, self.height, &mut self.msg_area);
            }
        }
        self.msg_area.draw(tb, colors, pos_x, pos_y);
    }

    pub(crate) fn keypressed(&mut self, key: Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('u') | Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('d') | Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            }

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            }

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            }

            Key::Tab => {
                if self.exit_dialogue.is_none() {
                    self.input_field.autocomplete(&self.nicks);
                }
                WidgetRet::KeyHandled
            }

            Key::Home => {
                self.msg_area.scroll_top();
                WidgetRet::KeyHandled
            }

            Key::End => {
                self.msg_area.scroll_bottom();
                WidgetRet::KeyHandled
            }

            key => {
                let ret = {
                    if let Some(exit_dialogue) = self.exit_dialogue.as_ref() {
                        exit_dialogue.keypressed(key)
                    } else {
                        self.input_field.keypressed(key)
                    }
                };

                if let WidgetRet::Remove = ret {
                    self.exit_dialogue = None;
                    WidgetRet::KeyHandled
                } else {
                    ret
                }
            }
        }
    }

    pub(crate) fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;

        self.input_field
            .resize(width, get_input_field_max_height(height));
        // msg_area should resize based on input_field's rendered height
        let msg_area_height = height - self.input_field.get_height(width);
        self.msg_area.resize(width, msg_area_height);

        // We don't show the nick in exit dialogue, so it has the full width
        for exit_dialogue in &mut self.exit_dialogue {
            exit_dialogue.resize(width);
        }
    }

    /// Get contents of the input field and cursor location and clear it.
    pub(crate) fn flush_input_field(&mut self) -> (String, i32) {
        self.input_field.flush()
    }

    /// Add a line to input field history.
    pub(crate) fn add_input_field_history(&mut self, str: &str) {
        self.input_field.add_history(str)
    }

    /// Set input field contents.
    pub(crate) fn set_input_field(&mut self, str: &str) {
        self.input_field.set(str)
    }

    /// Set cursor location in the input field.
    pub(crate) fn set_cursor(&mut self, cursor: i32) {
        self.input_field.set_cursor(cursor);
    }

    fn toggle_exit_dialogue(&mut self) {
        let exit_dialogue = ::std::mem::replace(&mut self.exit_dialogue, None);
        if exit_dialogue.is_none() {
            // We don't show the nick in exit dialogue, so it has the full width
            self.exit_dialogue = Some(ExitDialogue::new(self.width));
        }
    }
}

/// Calculation for input field's maximum height
fn get_input_field_max_height(window_height: i32) -> i32 {
    window_height / 2
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {
    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            let alignment = matches!(self.msg_area.layout(), Layout::Aligned { .. }); // for now
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            } else if alignment {
                Timestamp::blank(&mut self.msg_area)
            }
        } else {
            ts.stamp(&mut self.msg_area);
        }
        self.last_activity_ts = Some(ts);
    }

    pub(crate) fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area.add_text(topic, SegStyle::Topic);

        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_err_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::ErrMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_notify_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::Faded);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_client_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::UserMsg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        highlight: bool,
        is_action: bool,
    ) {
        // HACK: Some servers (bridges) don't send RPL_NAMREPLY and JOIN/PART messages but we still
        // want to support tab completion on those servers, so when we see a message from someone
        // we add the user to the nick list so that tab completion will complete their nick. See
        // #253 for details.
        self.nicks.insert(sender);

        self.reset_activity_line();
        self.add_timestamp(ts);

        let nick_color = self.get_nick_color(sender);
        let nick_col_style = SegStyle::NickColor(nick_color);

        // actions are /me msgs so they don't show the nick in the nick column, but in the msg
        let layout = self.msg_area.layout();
        let format_nick = |s: &str| -> String {
            if let Layout::Aligned { max_nick_len, .. } = layout {
                let mut aligned = format!("{:>padding$}", s, padding = max_nick_len);
                if aligned.len() > max_nick_len {
                    aligned.truncate(max_nick_len - 1);
                    aligned.push('…');
                }
                aligned
            } else {
                s.to_string()
            }
        };
        if is_action {
            self.msg_area
                .add_text(&format_nick("**"), SegStyle::UserMsg);
            // separator between nick and msg
            self.msg_area.add_text("  ", SegStyle::Faded);
            self.msg_area.add_text(sender, nick_col_style);
            // a space replacing the :
            self.msg_area.add_text(" ", SegStyle::UserMsg);
        } else {
            self.msg_area.add_text(&format_nick(sender), nick_col_style);
            // separator between nick and msg
            self.msg_area.add_text(": ", SegStyle::Faded);
        }

        let msg_style = if highlight {
            SegStyle::Highlight
        } else {
            SegStyle::UserMsg
        };

        self.msg_area.add_text(msg, msg_style);
        self.msg_area.set_current_line_alignment();
        self.msg_area.flush_line();
    }

    pub(crate) fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.add_text(msg, SegStyle::UserMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.add_text(msg, SegStyle::ErrMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn clear(&mut self) {
        self.msg_area.clear();
    }

    fn get_nick_color(&self, sender: &str) -> usize {
        // Anything works as long as it's fast
        let mut hash: usize = 5381;
        for c in sender.chars() {
            hash = hash.wrapping_mul(33).wrapping_add(c as usize);
        }
        hash
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub(crate) fn clear_nicks(&mut self) {
        self.nicks.clear();
    }

    pub(crate) fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.add_char('+', SegStyle::Join);
                    line.add_text(nick, SegStyle::Faded);
                });
            }
        }

        self.nicks.insert(nick);
    }

    pub(crate) fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        self.nicks.remove(nick);

        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.add_char('-', SegStyle::Part);
                    line.add_text(nick, SegStyle::Faded);
                });
            }
        }
    }

    /// `state` == `None` means toggle
    /// `state` == `Some(state)` means set it to `state`
    pub(crate) fn set_or_toggle_ignore(&mut self, state: Option<bool>) {
        self.show_status = state.unwrap_or(!self.show_status);
        if self.show_status {
            self.add_client_notify_msg("Ignore disabled");
        } else {
            self.add_client_notify_msg("Ignore enabled");
        }
    }

    pub(crate) fn is_showing_status(&self) -> bool {
        self.show_status
    }

    pub(crate) fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        self.nicks.remove(old_nick);
        self.nicks.insert(new_nick);

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.add_text(old_nick, SegStyle::Faded);
            line.add_char('>', SegStyle::Nick);
            line.add_text(new_nick, SegStyle::Faded);
        });
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        match self.last_activity_line {
            Some(ref l) if l.ts == ts => {
                let line_idx = l.line_idx;
                // FIXME: It's a bit hacky to add a space in this function which from the name
                // looks like a getter.
                // The idea is that we want to add a space *before* adding new stuff, not *after*,
                // to avoid adding redundant spaces. The test `small_screen_1` breaks if we don't
                // get this right.
                self.msg_area
                    .modify_line(line_idx, |line| line.add_char(' ', SegStyle::UserMsg));
                line_idx
            }
            _ => {
                self.add_timestamp(ts);
                if let Layout::Aligned {
                    max_nick_len,
                    msg_nick_sep_len,
                    ..
                } = self.msg_area.layout()
                {
                    self.msg_area.add_text(
                        &format!("{:^max$}", "", max = max_nick_len + msg_nick_sep_len),
                        SegStyle::UserMsg,
                    )
                }
                self.msg_area.set_current_line_alignment();
                let line_idx = self.msg_area.flush_line();
                self.last_activity_line = Some(ActivityLine { ts, line_idx });
                line_idx
            }
        }
    }
}
<commit_msg>Simplified nick formatting<commit_after>use term_input::Key;
use termbox_simple::Termbox;

use std::convert::From;

use time::{self, Tm};

use crate::config::Colors;
use crate::exit_dialogue::ExitDialogue;
use crate::input_area::InputArea;
use crate::msg_area::line::SegStyle;
use crate::msg_area::{Layout, MsgArea};
use crate::trie::Trie;
use crate::widget::WidgetRet;

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub(crate) struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area: MsgArea,

    // exit_dialogue handles input when available.
    // two fields (instead of an enum etc.) to avoid borrowchk problems
    input_field: InputArea,
    exit_dialogue: Option<ExitDialogue>,

    width: i32,
    height: i32,

    // Option to disable status messages ( join/part )
    show_status: bool,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    nicks: Trie,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub(crate) struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    /// The width of the timestamp plus a space
    pub(crate) const WIDTH: usize = 6;
    fn stamp(self, msg_area: &mut MsgArea) {
        msg_area.add_text(
            &format!("{:02}:{:02} ", self.hour, self.min),
            SegStyle::Timestamp,
        );
    }

    /// Inserts a blank space that is the size of a timestamp
    fn blank(msg_area: &mut MsgArea) {
        msg_area.add_text(
            &format!("{:^width$}", ' ', width = Timestamp::WIDTH),
            SegStyle::Timestamp,
        );
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp {
            hour: tm.tm_hour,
            min: tm.tm_min,
        }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub(crate) fn new(
        width: i32,
        height: i32,
        status: bool,
        scrollback: usize,
        msg_layout: Layout,
    ) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1, scrollback, msg_layout),
            input_field: InputArea::new(width, get_input_field_max_height(height)),
            exit_dialogue: None,
            width,
            height,
            show_status: status,
            nicks: Trie::new(),
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub(crate) fn set_nick(&mut self, nick: String) {
        let nick_color = self.get_nick_color(&nick);
        self.input_field.set_nick(nick, nick_color);
        // update text field size
        let w = self.width;
        let h = self.height;
        self.resize(w, h);
    }

    pub(crate) fn get_nick(&self) -> Option<String> {
        self.input_field.get_nick()
    }

    pub(crate) fn draw(&mut self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        match &self.exit_dialogue {
            Some(exit_dialogue) => {
                exit_dialogue.draw(tb, colors, pos_x, self.height - 1);
            }
            None => {
                // Draw InputArea first because it can trigger a resize of MsgArea
                self.input_field
                    .draw(tb, colors, pos_x, pos_y, self.height, &mut self.msg_area);
            }
        }
        self.msg_area.draw(tb, colors, pos_x, pos_y);
    }

    pub(crate) fn keypressed(&mut self, key: Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('u') | Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('d') | Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            }

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            }

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            }

            Key::Tab => {
                if self.exit_dialogue.is_none() {
                    self.input_field.autocomplete(&self.nicks);
                }
                WidgetRet::KeyHandled
            }

            Key::Home => {
                self.msg_area.scroll_top();
                WidgetRet::KeyHandled
            }

            Key::End => {
                self.msg_area.scroll_bottom();
                WidgetRet::KeyHandled
            }

            key => {
                let ret = {
                    if let Some(exit_dialogue) = self.exit_dialogue.as_ref() {
                        exit_dialogue.keypressed(key)
                    } else {
                        self.input_field.keypressed(key)
                    }
                };

                if let WidgetRet::Remove = ret {
                    self.exit_dialogue = None;
                    WidgetRet::KeyHandled
                } else {
                    ret
                }
            }
        }
    }

    pub(crate) fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;

        self.input_field
            .resize(width, get_input_field_max_height(height));
        // msg_area should resize based on input_field's rendered height
        let msg_area_height = height - self.input_field.get_height(width);
        self.msg_area.resize(width, msg_area_height);

        // We don't show the nick in exit dialogue, so it has the full width
        for exit_dialogue in &mut self.exit_dialogue {
            exit_dialogue.resize(width);
        }
    }

    /// Get contents of the input field and cursor location and clear it.
    pub(crate) fn flush_input_field(&mut self) -> (String, i32) {
        self.input_field.flush()
    }

    /// Add a line to input field history.
    pub(crate) fn add_input_field_history(&mut self, str: &str) {
        self.input_field.add_history(str)
    }

    /// Set input field contents.
    pub(crate) fn set_input_field(&mut self, str: &str) {
        self.input_field.set(str)
    }

    /// Set cursor location in the input field.
    pub(crate) fn set_cursor(&mut self, cursor: i32) {
        self.input_field.set_cursor(cursor);
    }

    fn toggle_exit_dialogue(&mut self) {
        let exit_dialogue = ::std::mem::replace(&mut self.exit_dialogue, None);
        if exit_dialogue.is_none() {
            // We don't show the nick in exit dialogue, so it has the full width
            self.exit_dialogue = Some(ExitDialogue::new(self.width));
        }
    }
}

/// Calculation for input field's maximum height
fn get_input_field_max_height(window_height: i32) -> i32 {
    window_height / 2
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {
    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            let alignment = matches!(self.msg_area.layout(), Layout::Aligned { .. }); // for now
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            } else if alignment {
                Timestamp::blank(&mut self.msg_area)
            }
        } else {
            ts.stamp(&mut self.msg_area);
        }
        self.last_activity_ts = Some(ts);
    }

    pub(crate) fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area.add_text(topic, SegStyle::Topic);

        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_err_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::ErrMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_notify_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::Faded);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_client_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area.add_text(msg, SegStyle::UserMsg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        highlight: bool,
        is_action: bool,
    ) {
        // HACK: Some servers (bridges) don't send RPL_NAMREPLY and JOIN/PART messages but we still
        // want to support tab completion on those servers, so when we see a message from someone
        // we add the user to the nick list so that tab completion will complete their nick. See
        // #253 for details.
        self.nicks.insert(sender);

        self.reset_activity_line();
        self.add_timestamp(ts);

        let nick_color = self.get_nick_color(sender);
        let nick_col_style = SegStyle::NickColor(nick_color);

        // actions are /me msgs so they don't show the nick in the nick column, but in the msg
        let layout = self.msg_area.layout();
        let format_nick = |s: &str| -> String {
            if let Layout::Aligned { max_nick_len, .. } = layout {
                let mut aligned = format!("{:>padding$.padding$}", s, padding = max_nick_len);
                if s.len() > max_nick_len {
                    aligned.pop();
                    aligned.push('…');
                }
                aligned
            } else {
                s.to_string()
            }
        };
        if is_action {
            self.msg_area
                .add_text(&format_nick("**"), SegStyle::UserMsg);
            // separator between nick and msg
            self.msg_area.add_text("  ", SegStyle::Faded);
            self.msg_area.add_text(sender, nick_col_style);
            // a space replacing the :
            self.msg_area.add_text(" ", SegStyle::UserMsg);
        } else {
            self.msg_area.add_text(&format_nick(sender), nick_col_style);
            // separator between nick and msg
            self.msg_area.add_text(": ", SegStyle::Faded);
        }

        let msg_style = if highlight {
            SegStyle::Highlight
        } else {
            SegStyle::UserMsg
        };

        self.msg_area.add_text(msg, msg_style);
        self.msg_area.set_current_line_alignment();
        self.msg_area.flush_line();
    }

    pub(crate) fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.add_text(msg, SegStyle::UserMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.add_text(msg, SegStyle::ErrMsg);
        self.msg_area.flush_line();
    }

    pub(crate) fn clear(&mut self) {
        self.msg_area.clear();
    }

    fn get_nick_color(&self, sender: &str) -> usize {
        // Anything works as long as it's fast
        let mut hash: usize = 5381;
        for c in sender.chars() {
            hash = hash.wrapping_mul(33).wrapping_add(c as usize);
        }
        hash
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub(crate) fn clear_nicks(&mut self) {
        self.nicks.clear();
    }

    pub(crate) fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.add_char('+', SegStyle::Join);
                    line.add_text(nick, SegStyle::Faded);
                });
            }
        }

        self.nicks.insert(nick);
    }

    pub(crate) fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        self.nicks.remove(nick);

        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.add_char('-', SegStyle::Part);
                    line.add_text(nick, SegStyle::Faded);
                });
            }
        }
    }

    /// `state` == `None` means toggle
    /// `state` == `Some(state)` means set it to `state`
    pub(crate) fn set_or_toggle_ignore(&mut self, state: Option<bool>) {
        self.show_status = state.unwrap_or(!self.show_status);
        if self.show_status {
            self.add_client_notify_msg("Ignore disabled");
        } else {
            self.add_client_notify_msg("Ignore enabled");
        }
    }

    pub(crate) fn is_showing_status(&self) -> bool {
        self.show_status
    }

    pub(crate) fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        self.nicks.remove(old_nick);
        self.nicks.insert(new_nick);

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.add_text(old_nick, SegStyle::Faded);
            line.add_char('>', SegStyle::Nick);
            line.add_text(new_nick, SegStyle::Faded);
        });
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        match self.last_activity_line {
            Some(ref l) if l.ts == ts => {
                let line_idx = l.line_idx;
                // FIXME: It's a bit hacky to add a space in this function which from the name
                // looks like a getter.
                // The idea is that we want to add a space *before* adding new stuff, not *after*,
                // to avoid adding redundant spaces. The test `small_screen_1` breaks if we don't
                // get this right.
                self.msg_area
                    .modify_line(line_idx, |line| line.add_char(' ', SegStyle::UserMsg));
                line_idx
            }
            _ => {
                self.add_timestamp(ts);
                if let Layout::Aligned {
                    max_nick_len,
                    msg_nick_sep_len,
                    ..
                } = self.msg_area.layout()
                {
                    self.msg_area.add_text(
                        &format!("{:^max$}", "", max = max_nick_len + msg_nick_sep_len),
                        SegStyle::UserMsg,
                    )
                }
                self.msg_area.set_current_line_alignment();
                let line_idx = self.msg_area.flush_line();
                self.last_activity_line = Some(ActivityLine { ts, line_idx });
                line_idx
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate extra;

use std::{io, str, env};
use std::io::Write;
use std::fs::File;
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::WriteExt;

static MAN_PAGE: &'static str = /* @MANSTART{grep} */ r#"
NAME
    keymap - change the keymap

SYNOPSIS
    keymap [-h | --help] [-l --list] NAME

DESCRIPTION
    Changes the keymap.
OPTIONS
    -h
    --help
        Print this manual page.

    -l
    --list
        List available keymaps.
"#; /* @MANEND */

fn main() {
    let mut args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let arg  = match args.next() {
        Some(arg) => arg,
        None => {
            // TODO Write explanation
            println!("Needs one argument.");
            exit(1);
        }
    };
    let path = if arg.starts_with("-") {
        match arg.as_str() {
            "-h" | "--help" => {
                stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
            },
            "-l" | "--list" => {
                // TODO list keymaps
            },
            _ => {
                stderr.write(b"Unknown option: ").try(&mut stderr);
                stderr.write(arg.as_bytes()).try(&mut stderr);
                stderr.write(b"\n").try(&mut stderr);
                let _ = stderr.flush();
                exit(1);
            }
        }
        exit(0);
    } else {
        arg
    };

    match File::open("display:keymap") {
        Ok(mut file) => {
            match file.write(path.as_bytes()) {
                Err(e) => println!("keymap: could not change keymap: {}", e),
                _ => {}
            }

        },
        Err(err) => {
            println!("keymap: failed to open display: {}", err);
            exit(1);
        }
    }
}
<commit_msg>Fix man page names<commit_after>extern crate extra;

use std::{io, str, env};
use std::io::Write;
use std::fs::File;
use std::process::exit;

use extra::option::OptionalExt;
use extra::io::WriteExt;

static MAN_PAGE: &'static str = /* @MANSTART{keymap} */ r#"
NAME
    keymap - change the keymap

SYNOPSIS
    keymap [-h | --help] [-l --list] NAME

DESCRIPTION
    Changes the keymap.
OPTIONS
    -h
    --help
        Print this manual page.

    -l
    --list
        List available keymaps.
"#; /* @MANEND */

fn main() {
    let mut args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let arg  = match args.next() {
        Some(arg) => arg,
        None => {
            // TODO Write explanation
            println!("Needs one argument.");
            exit(1);
        }
    };
    let path = if arg.starts_with("-") {
        match arg.as_str() {
            "-h" | "--help" => {
                stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
            },
            "-l" | "--list" => {
                // TODO list keymaps
            },
            _ => {
                stderr.write(b"Unknown option: ").try(&mut stderr);
                stderr.write(arg.as_bytes()).try(&mut stderr);
                stderr.write(b"\n").try(&mut stderr);
                let _ = stderr.flush();
                exit(1);
            }
        }
        exit(0);
    } else {
        arg
    };

    match File::open("display:keymap") {
        Ok(mut file) => {
            match file.write(path.as_bytes()) {
                Err(e) => println!("keymap: could not change keymap: {}", e),
                _ => {}
            }

        },
        Err(err) => {
            println!("keymap: failed to open display: {}", err);
            exit(1);
        }
    }
}
<|endoftext|>