<commit_before>use gl;
use glutin;
use native::NativeTaskBuilder;
use std::sync::atomic::{AtomicUint, Relaxed};
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;
use GliumCreationError;

enum Message {
    EndFrame,
    Execute(proc(CommandContext):Send),
}

pub struct Context {
    commands: Mutex<Sender<Message>>,
    events: Mutex<Receiver<glutin::Event>>,

    /// Dimensions of the frame buffer.
    dimensions: Arc<(AtomicUint, AtomicUint)>,
}

pub struct CommandContext<'a, 'b> {
    pub gl: &'a gl::Gl,
    pub state: &'b mut GLState,
    pub version: &'a GlVersion,
    pub extensions: &'a ExtensionsList,
    pub opengl_es: bool,
}

/// Represents the current OpenGL ctxt.state.
/// The current state is passed to each function and can be freely updated.
pub struct GLState {
    /// Whether GL_BLEND is enabled
    pub enabled_blend: bool,

    /// Whether GL_CULL_FACE is enabled
    pub enabled_cull_face: bool,

    /// Whether GL_DEBUG_OUTPUT is enabled. None means "unknown".
    pub enabled_debug_output: Option<bool>,

    /// Whether GL_DEBUG_OUTPUT_SYNCHRONOUS is enabled
    pub enabled_debug_output_synchronous: bool,

    /// Whether GL_DEPTH_TEST is enabled
    pub enabled_depth_test: bool,

    /// Whether GL_DITHER is enabled
    pub enabled_dither: bool,

    /// Whether GL_POLYGON_OFFSET_FILL is enabled
    pub enabled_polygon_offset_fill: bool,

    /// Whether GL_SAMPLE_ALPHA_TO_COVERAGE is enabled
    pub enabled_sample_alpha_to_coverage: bool,

    /// Whether GL_SAMPLE_COVERAGE is enabled
    pub enabled_sample_coverage: bool,

    /// Whether GL_SCISSOR_TEST is enabled
    pub enabled_scissor_test: bool,

    /// Whether GL_STENCIL_TEST is enabled
    pub enabled_stencil_test: bool,

    // The latest value passed to `glUseProgram`.
    pub program: gl::types::GLuint,

    // The latest value passed to `glBindVertexArray`.
    pub vertex_array: gl::types::GLuint,

    // The latest value passed to `glClearColor`.
    pub clear_color: (gl::types::GLclampf, gl::types::GLclampf,
                      gl::types::GLclampf, gl::types::GLclampf),

    // The latest value passed to `glClearDepthf`.
    pub clear_depth: gl::types::GLclampf,

    // The latest value passed to `glClearStencil`.
    pub clear_stencil: gl::types::GLint,

    /// The latest buffer bound to `GL_ARRAY_BUFFER`.
    pub array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_ELEMENT_ARRAY_BUFFER`.
    pub element_array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_PIXEL_PACK_BUFFER`.
    pub pixel_pack_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_PIXEL_UNPACK_BUFFER`.
    pub pixel_unpack_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_READ_FRAMEBUFFER`.
    pub read_framebuffer: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_DRAW_FRAMEBUFFER`.
    pub draw_framebuffer: Option<gl::types::GLuint>,

    /// The latest render buffer bound with `glBindRenderbuffer`.
    pub renderbuffer: Option<gl::types::GLuint>,

    /// The latest values passed to `glBlendFunc`.
    pub blend_func: (gl::types::GLenum, gl::types::GLenum),

    /// The latest value passed to `glDepthFunc`.
    pub depth_func: gl::types::GLenum,

    /// The latest values passed to `glViewport`.
    pub viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei),

    /// The latest value passed to `glLineWidth`.
    pub line_width: gl::types::GLfloat,

    /// The latest value passed to `glCullFace`.
    pub cull_face: gl::types::GLenum,

    /// The latest value passed to `glPolygonMode`.
    pub polygon_mode: gl::types::GLenum,
}

impl GLState {
    /// Builds the `GLState` corresponding to the default GL values.
    fn new_defaults(viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei,
        gl::types::GLsizei)) -> GLState
    {
        GLState {
            enabled_blend: false,
            enabled_cull_face: false,
            enabled_debug_output: None,
            enabled_debug_output_synchronous: false,
            enabled_depth_test: false,
            enabled_dither: false,
            enabled_polygon_offset_fill: false,
            enabled_sample_alpha_to_coverage: false,
            enabled_sample_coverage: false,
            enabled_scissor_test: false,
            enabled_stencil_test: false,

            program: 0,
            vertex_array: 0,
            clear_color: (0.0, 0.0, 0.0, 0.0),
            clear_depth: 1.0,
            clear_stencil: 0,
            array_buffer_binding: None,
            element_array_buffer_binding: None,
            pixel_pack_buffer_binding: None,
            pixel_unpack_buffer_binding: None,
            read_framebuffer: None,
            draw_framebuffer: None,
            renderbuffer: None,
            depth_func: gl::LESS,
            blend_func: (0, 0),     // no default specified
            viewport: viewport,
            line_width: 1.0,
            cull_face: gl::BACK,
            polygon_mode: gl::FILL,
        }
    }
}

/// Describes an OpenGL ctxt.version.
#[deriving(Show, Clone, PartialEq, Eq)]
pub struct GlVersion(pub u8, pub u8);

impl PartialOrd for GlVersion {
    fn partial_cmp(&self, other: &GlVersion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GlVersion {
    fn cmp(&self, other: &GlVersion) -> Ordering {
        match self.0.cmp(&other.0) {
            Equal => self.1.cmp(&other.1),
            a => a
        }
    }
}

/// Contains data about the list of extensions
pub struct ExtensionsList {
    /// GL_EXT_direct_state_access
    pub gl_ext_direct_state_access: bool,
    /// GL_EXT_framebuffer_object
    pub gl_ext_framebuffer_object: bool,
    /// GL_EXT_geometry_shader4
    pub gl_ext_geometry_shader4: bool,
    /// GL_EXT_framebuffer_blit
    pub gl_ext_framebuffer_blit: bool,
    /// GL_KHR_debug
    pub gl_khr_debug: bool,
    /// GL_NVX_gpu_memory_info
    pub gl_nvx_gpu_memory_info: bool,
    /// GL_ATI_meminfo
    pub gl_ati_meminfo: bool,
}

impl Context {
    pub fn new_from_window(window: glutin::WindowBuilder) -> Result<Context, GliumCreationError> {
        let (tx_events, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions.clone(),
        };

        let (tx_success, rx_success) = channel();

        TaskBuilder::new().native().spawn(proc() {
            let window = match window.build() {
                Ok(w) => w,
                Err(e) => {
                    tx_success.send(Err(e));
                    return;
                }
            };

            unsafe { window.make_current(); }
            tx_success.send(Ok(()));

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState and modifying to GL state to match it
            let mut gl_state = {
                let viewport = {
                    let dim = window.get_inner_size().unwrap();
                    dimensions.0.store(dim.0, Relaxed);
                    dimensions.1.store(dim.1, Relaxed);
                    (0, 0, dim.0 as gl::types::GLsizei, dim.1 as gl::types::GLsizei)
                };

                GLState::new_defaults(viewport)
            };

            // getting the GL version and extensions
            let version = get_gl_version(&gl);
            let extensions = get_extensions(&gl);

            // main loop
            'main: loop {
                // processing commands
                loop {
                    match rx_commands.recv_opt() {
                        Ok(Message::EndFrame) => break,
                        Ok(Message::Execute(cmd)) => cmd(CommandContext {
                            gl:&gl, state: &mut gl_state, version: &version, extensions: &extensions,
                            opengl_es: false,
                        }),
                        Err(_) => break 'main
                    }
                }

                // this is necessary on Windows 8, or nothing is being displayed
                unsafe { gl.Flush(); }

                // swapping
                window.swap_buffers();

                // getting events
                for event in window.poll_events() {
                    // calling `glViewport` if the window has been resized
                    if let &glutin::Event::Resized(width, height) = &event {
                        dimensions.0.store(width, Relaxed);
                        dimensions.1.store(height, Relaxed);

                        if gl_state.viewport != (0, 0, width as gl::types::GLsizei,
                                                 height as gl::types::GLsizei)
                        {
                            unsafe {
                                gl.Viewport(0, 0, width as gl::types::GLsizei,
                                    height as gl::types::GLsizei);
                                gl_state.viewport = (0, 0, width as gl::types::GLsizei,
                                    height as gl::types::GLsizei);
                            }
                        }
                    }

                    // sending the event outside
                    if tx_events.send_opt(event.clone()).is_err() {
                        break 'main;
                    }
                }
            }
        });

        try!(rx_success.recv());
        Ok(context)
    }

    pub fn new_from_headless(window: glutin::HeadlessRendererBuilder)
        -> Result<Context, GliumCreationError>
    {
        let (_, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        // TODO: fixme
        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions,
        };

        let (tx_success, rx_success) = channel();

        TaskBuilder::new().native().spawn(proc() {
            let window = match window.build() {
                Ok(w) => w,
                Err(e) => {
                    tx_success.send(Err(e));
                    return;
                }
            };
            unsafe { window.make_current(); }
            tx_success.send(Ok(()));

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState, version, and extensions
            let mut gl_state = GLState::new_defaults((0, 0, 0, 0));    // FIXME:
            let version = get_gl_version(&gl);
            let extensions = get_extensions(&gl);

            loop {
                match rx_commands.recv_opt() {
                    Ok(Message::Execute(cmd)) => cmd(CommandContext {
                        gl:&gl, state: &mut gl_state, version: &version, extensions: &extensions,
                        opengl_es: false,
                    }),
                    Ok(Message::EndFrame) => (),     // ignoring buffer swapping
                    Err(_) => break
                }
            }
        });

        try!(rx_success.recv());
        Ok(context)
    }

    pub fn get_framebuffer_dimensions(&self) -> (uint, uint) {
        (
            self.dimensions.0.load(Relaxed),
            self.dimensions.1.load(Relaxed),
        )
    }

    pub fn exec(&self, f: proc(CommandContext): Send) {
        self.commands.lock().send(Message::Execute(f));
    }

    pub fn swap_buffers(&self) {
        self.commands.lock().send(Message::EndFrame);
    }

    pub fn recv(&self) -> Vec<glutin::Event> {
        let events = self.events.lock();

        let mut result = Vec::new();
        loop {
            match events.try_recv() {
                Ok(ev) => result.push(ev),
                Err(_) => break
            }
        }
        result
    }
}

fn get_gl_version(gl: &gl::Gl) -> GlVersion {
    use std::c_str::CString;

    unsafe {
        let version = gl.GetString(gl::VERSION);
        let version = CString::new(version as *const i8, false);
        let version = version.as_str().expect("OpenGL version contains non-utf8 characters");

        let version = version.words().next().expect("glGetString(GL_VERSION) returned an empty \
                                                     string");

        let mut iter = version.split(|c: char| c == '.');
        let major = iter.next().unwrap();
        let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

        GlVersion(
            from_str(major).expect("failed to parse GL major version"),
            from_str(minor).expect("failed to parse GL minor version"),
        )
    }
}

fn get_extensions_strings(gl: &gl::Gl) -> Vec<String> {
    use std::c_str::CString;

    unsafe {
        let list = gl.GetString(gl::EXTENSIONS);

        if list.is_null() {
            let mut num_extensions = 0;
            gl.GetIntegerv(gl::NUM_EXTENSIONS, &mut num_extensions);

            range(0, num_extensions).map(|num| {
                let ext = gl.GetStringi(gl::EXTENSIONS, num as gl::types::GLuint);
                let ext = CString::new(ext as *const i8, false);
                ext.as_str().expect("OpenGL extension contains non-utf8 characters").to_string()
            }).collect()

        } else {
            let list = CString::new(list as *const i8, false);
            let list = list.as_str()
                           .expect("List of OpenGL extensions contains non-utf8 characters");
            list.words().map(|e| e.to_string()).collect()
        }
    }
}

fn get_extensions(gl: &gl::Gl) -> ExtensionsList {
    let strings = get_extensions_strings(gl);

    let mut extensions = ExtensionsList {
        gl_ext_direct_state_access: false,
        gl_ext_framebuffer_object: false,
        gl_ext_geometry_shader4: false,
        gl_ext_framebuffer_blit: false,
        gl_khr_debug: false,
        gl_nvx_gpu_memory_info: false,
        gl_ati_meminfo: false,
    };

    for extension in strings.into_iter() {
        match extension.as_slice() {
            "GL_EXT_direct_state_access" => extensions.gl_ext_direct_state_access = true,
            "GL_EXT_framebuffer_object" => extensions.gl_ext_framebuffer_object = true,
            "GL_EXT_geometry_shader4" => extensions.gl_ext_geometry_shader4 = true,
            "GL_EXT_framebuffer_blit" => extensions.gl_ext_framebuffer_blit = true,
            "GL_KHR_debug" => extensions.gl_khr_debug = true,
            "GL_NVX_gpu_memory_info" => extensions.gl_nvx_gpu_memory_info = true,
            "GL_ATI_meminfo" => extensions.gl_ati_meminfo = true,
            _ => ()
        }
    }

    extensions
}
<commit_msg>Add capabilities struct<commit_after>use gl;
use glutin;
use native::NativeTaskBuilder;
use std::sync::atomic::{AtomicUint, Relaxed};
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;
use GliumCreationError;

enum Message {
    EndFrame,
    Execute(proc(CommandContext):Send),
}

pub struct Context {
    commands: Mutex<Sender<Message>>,
    events: Mutex<Receiver<glutin::Event>>,

    /// Dimensions of the frame buffer.
    dimensions: Arc<(AtomicUint, AtomicUint)>,
}

pub struct CommandContext<'a, 'b> {
    pub gl: &'a gl::Gl,
    pub state: &'b mut GLState,
    pub version: &'a GlVersion,
    pub extensions: &'a ExtensionsList,
    pub opengl_es: bool,
    pub capabilities: &'a Capabilities,
}

/// Represents the current OpenGL ctxt.state.
/// The current state is passed to each function and can be freely updated.
pub struct GLState {
    /// Whether GL_BLEND is enabled
    pub enabled_blend: bool,

    /// Whether GL_CULL_FACE is enabled
    pub enabled_cull_face: bool,

    /// Whether GL_DEBUG_OUTPUT is enabled. None means "unknown".
    pub enabled_debug_output: Option<bool>,

    /// Whether GL_DEBUG_OUTPUT_SYNCHRONOUS is enabled
    pub enabled_debug_output_synchronous: bool,

    /// Whether GL_DEPTH_TEST is enabled
    pub enabled_depth_test: bool,

    /// Whether GL_DITHER is enabled
    pub enabled_dither: bool,

    /// Whether GL_POLYGON_OFFSET_FILL is enabled
    pub enabled_polygon_offset_fill: bool,

    /// Whether GL_SAMPLE_ALPHA_TO_COVERAGE is enabled
    pub enabled_sample_alpha_to_coverage: bool,

    /// Whether GL_SAMPLE_COVERAGE is enabled
    pub enabled_sample_coverage: bool,

    /// Whether GL_SCISSOR_TEST is enabled
    pub enabled_scissor_test: bool,

    /// Whether GL_STENCIL_TEST is enabled
    pub enabled_stencil_test: bool,

    // The latest value passed to `glUseProgram`.
    pub program: gl::types::GLuint,

    // The latest value passed to `glBindVertexArray`.
    pub vertex_array: gl::types::GLuint,

    // The latest value passed to `glClearColor`.
    pub clear_color: (gl::types::GLclampf, gl::types::GLclampf,
                      gl::types::GLclampf, gl::types::GLclampf),

    // The latest value passed to `glClearDepthf`.
    pub clear_depth: gl::types::GLclampf,

    // The latest value passed to `glClearStencil`.
    pub clear_stencil: gl::types::GLint,

    /// The latest buffer bound to `GL_ARRAY_BUFFER`.
    pub array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_ELEMENT_ARRAY_BUFFER`.
    pub element_array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_PIXEL_PACK_BUFFER`.
    pub pixel_pack_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_PIXEL_UNPACK_BUFFER`.
    pub pixel_unpack_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_READ_FRAMEBUFFER`.
    pub read_framebuffer: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_DRAW_FRAMEBUFFER`.
    pub draw_framebuffer: Option<gl::types::GLuint>,

    /// The latest render buffer bound with `glBindRenderbuffer`.
    pub renderbuffer: Option<gl::types::GLuint>,

    /// The latest values passed to `glBlendFunc`.
    pub blend_func: (gl::types::GLenum, gl::types::GLenum),

    /// The latest value passed to `glDepthFunc`.
    pub depth_func: gl::types::GLenum,

    /// The latest values passed to `glViewport`.
    pub viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei),

    /// The latest value passed to `glLineWidth`.
    pub line_width: gl::types::GLfloat,

    /// The latest value passed to `glCullFace`.
    pub cull_face: gl::types::GLenum,

    /// The latest value passed to `glPolygonMode`.
    pub polygon_mode: gl::types::GLenum,
}

impl GLState {
    /// Builds the `GLState` corresponding to the default GL values.
    fn new_defaults(viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei,
        gl::types::GLsizei)) -> GLState
    {
        GLState {
            enabled_blend: false,
            enabled_cull_face: false,
            enabled_debug_output: None,
            enabled_debug_output_synchronous: false,
            enabled_depth_test: false,
            enabled_dither: false,
            enabled_polygon_offset_fill: false,
            enabled_sample_alpha_to_coverage: false,
            enabled_sample_coverage: false,
            enabled_scissor_test: false,
            enabled_stencil_test: false,

            program: 0,
            vertex_array: 0,
            clear_color: (0.0, 0.0, 0.0, 0.0),
            clear_depth: 1.0,
            clear_stencil: 0,
            array_buffer_binding: None,
            element_array_buffer_binding: None,
            pixel_pack_buffer_binding: None,
            pixel_unpack_buffer_binding: None,
            read_framebuffer: None,
            draw_framebuffer: None,
            renderbuffer: None,
            depth_func: gl::LESS,
            blend_func: (0, 0),     // no default specified
            viewport: viewport,
            line_width: 1.0,
            cull_face: gl::BACK,
            polygon_mode: gl::FILL,
        }
    }
}

/// Describes an OpenGL ctxt.version.
#[deriving(Show, Clone, PartialEq, Eq)]
pub struct GlVersion(pub u8, pub u8);

impl PartialOrd for GlVersion {
    fn partial_cmp(&self, other: &GlVersion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GlVersion {
    fn cmp(&self, other: &GlVersion) -> Ordering {
        match self.0.cmp(&other.0) {
            Equal => self.1.cmp(&other.1),
            a => a
        }
    }
}

/// Contains data about the list of extensions
pub struct ExtensionsList {
    /// GL_EXT_direct_state_access
    pub gl_ext_direct_state_access: bool,
    /// GL_EXT_framebuffer_object
    pub gl_ext_framebuffer_object: bool,
    /// GL_EXT_geometry_shader4
    pub gl_ext_geometry_shader4: bool,
    /// GL_EXT_framebuffer_blit
    pub gl_ext_framebuffer_blit: bool,
    /// GL_KHR_debug
    pub gl_khr_debug: bool,
    /// GL_NVX_gpu_memory_info
    pub gl_nvx_gpu_memory_info: bool,
    /// GL_ATI_meminfo
    pub gl_ati_meminfo: bool,
}

/// Represents the capabilities of the context.
pub struct Capabilities {
    /// True if the context supports left and right buffers.
    pub stereo: bool,
}

impl Context {
    pub fn new_from_window(window: glutin::WindowBuilder) -> Result<Context, GliumCreationError> {
        let (tx_events, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions.clone(),
        };

        let (tx_success, rx_success) = channel();

        TaskBuilder::new().native().spawn(proc() {
            let window = match window.build() {
                Ok(w) => w,
                Err(e) => {
                    tx_success.send(Err(e));
                    return;
                }
            };

            unsafe { window.make_current(); }
            tx_success.send(Ok(()));

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState and modifying to GL state to match it
            let mut gl_state = {
                let viewport = {
                    let dim = window.get_inner_size().unwrap();
                    dimensions.0.store(dim.0, Relaxed);
                    dimensions.1.store(dim.1, Relaxed);
                    (0, 0, dim.0 as gl::types::GLsizei, dim.1 as gl::types::GLsizei)
                };

                GLState::new_defaults(viewport)
            };

            // getting the GL version and extensions
            let opengl_es = false;
            let version = get_gl_version(&gl);
            let capabilities = get_capabilities(&gl, opengl_es);
            let extensions = get_extensions(&gl);

            // main loop
            'main: loop {
                // processing commands
                loop {
                    match rx_commands.recv_opt() {
                        Ok(Message::EndFrame) => break,
                        Ok(Message::Execute(cmd)) => cmd(CommandContext {
                            gl:&gl, state: &mut gl_state, version: &version, extensions: &extensions,
                            opengl_es: opengl_es, capabilities: &capabilities,
                        }),
                        Err(_) => break 'main
                    }
                }

                // this is necessary on Windows 8, or nothing is being displayed
                unsafe { gl.Flush(); }

                // swapping
                window.swap_buffers();

                // getting events
                for event in window.poll_events() {
                    // calling `glViewport` if the window has been resized
                    if let &glutin::Event::Resized(width, height) = &event {
                        dimensions.0.store(width, Relaxed);
                        dimensions.1.store(height, Relaxed);

                        if gl_state.viewport != (0, 0, width as gl::types::GLsizei,
                                                 height as gl::types::GLsizei)
                        {
                            unsafe {
                                gl.Viewport(0, 0, width as gl::types::GLsizei,
                                    height as gl::types::GLsizei);
                                gl_state.viewport = (0, 0, width as gl::types::GLsizei,
                                    height as gl::types::GLsizei);
                            }
                        }
                    }

                    // sending the event outside
                    if tx_events.send_opt(event.clone()).is_err() {
                        break 'main;
                    }
                }
            }
        });

        try!(rx_success.recv());
        Ok(context)
    }

    pub fn new_from_headless(window: glutin::HeadlessRendererBuilder)
        -> Result<Context, GliumCreationError>
    {
        let (_, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        // TODO: fixme
        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions,
        };

        let (tx_success, rx_success) = channel();

        TaskBuilder::new().native().spawn(proc() {
            let window = match window.build() {
                Ok(w) => w,
                Err(e) => {
                    tx_success.send(Err(e));
                    return;
                }
            };
            unsafe { window.make_current(); }
            tx_success.send(Ok(()));

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState, version, and extensions
            let mut gl_state = GLState::new_defaults((0, 0, 0, 0));    // FIXME:
            let opengl_es = false;
            let version = get_gl_version(&gl);
            let extensions = get_extensions(&gl);
            let capabilities = get_capabilities(&gl, opengl_es);

            loop {
                match rx_commands.recv_opt() {
                    Ok(Message::Execute(cmd)) => cmd(CommandContext {
                        gl:&gl, state: &mut gl_state, version: &version, extensions: &extensions,
                        opengl_es: opengl_es, capabilities: &capabilities,
                    }),
                    Ok(Message::EndFrame) => (),     // ignoring buffer swapping
                    Err(_) => break
                }
            }
        });

        try!(rx_success.recv());
        Ok(context)
    }

    pub fn get_framebuffer_dimensions(&self) -> (uint, uint) {
        (
            self.dimensions.0.load(Relaxed),
            self.dimensions.1.load(Relaxed),
        )
    }

    pub fn exec(&self, f: proc(CommandContext): Send) {
        self.commands.lock().send(Message::Execute(f));
    }

    pub fn swap_buffers(&self) {
        self.commands.lock().send(Message::EndFrame);
    }

    pub fn recv(&self) -> Vec<glutin::Event> {
        let events = self.events.lock();

        let mut result = Vec::new();
        loop {
            match events.try_recv() {
                Ok(ev) => result.push(ev),
                Err(_) => break
            }
        }
        result
    }
}

fn get_gl_version(gl: &gl::Gl) -> GlVersion {
    use std::c_str::CString;

    unsafe {
        let version = gl.GetString(gl::VERSION);
        let version = CString::new(version as *const i8, false);
        let version = version.as_str().expect("OpenGL version contains non-utf8 characters");

        let version = version.words().next().expect("glGetString(GL_VERSION) returned an empty \
                                                     string");

        let mut iter = version.split(|c: char| c == '.');
        let major = iter.next().unwrap();
        let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

        GlVersion(
            from_str(major).expect("failed to parse GL major version"),
            from_str(minor).expect("failed to parse GL minor version"),
        )
    }
}

fn get_extensions_strings(gl: &gl::Gl) -> Vec<String> {
    use std::c_str::CString;

    unsafe {
        let list = gl.GetString(gl::EXTENSIONS);

        if list.is_null() {
            let mut num_extensions = 0;
            gl.GetIntegerv(gl::NUM_EXTENSIONS, &mut num_extensions);

            range(0, num_extensions).map(|num| {
                let ext = gl.GetStringi(gl::EXTENSIONS, num as gl::types::GLuint);
                let ext = CString::new(ext as *const i8, false);
                ext.as_str().expect("OpenGL extension contains non-utf8 characters").to_string()
            }).collect()

        } else {
            let list = CString::new(list as *const i8, false);
            let list = list.as_str()
                           .expect("List of OpenGL extensions contains non-utf8 characters");
            list.words().map(|e| e.to_string()).collect()
        }
    }
}

fn get_extensions(gl: &gl::Gl) -> ExtensionsList {
    let strings = get_extensions_strings(gl);

    let mut extensions = ExtensionsList {
        gl_ext_direct_state_access: false,
        gl_ext_framebuffer_object: false,
        gl_ext_geometry_shader4: false,
        gl_ext_framebuffer_blit: false,
        gl_khr_debug: false,
        gl_nvx_gpu_memory_info: false,
        gl_ati_meminfo: false,
    };

    for extension in strings.into_iter() {
        match extension.as_slice() {
            "GL_EXT_direct_state_access" => extensions.gl_ext_direct_state_access = true,
            "GL_EXT_framebuffer_object" => extensions.gl_ext_framebuffer_object = true,
            "GL_EXT_geometry_shader4" => extensions.gl_ext_geometry_shader4 = true,
            "GL_EXT_framebuffer_blit" => extensions.gl_ext_framebuffer_blit = true,
            "GL_KHR_debug" => extensions.gl_khr_debug = true,
            "GL_NVX_gpu_memory_info" => extensions.gl_nvx_gpu_memory_info = true,
            "GL_ATI_meminfo" => extensions.gl_ati_meminfo = true,
            _ => ()
        }
    }

    extensions
}

fn get_capabilities(gl: &gl::Gl, gl_es: bool) -> Capabilities {
    Capabilities {
        stereo: unsafe {
            if gl_es {
                false
            } else {
                use std::mem;
                let mut val: gl::types::GLboolean = mem::uninitialized();
                gl.GetBooleanv(gl::STEREO, &mut val);
                val != 0
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Clean up the construction of the VkClearValue objects for colour and depth/stencil<commit_after><|endoftext|>


<commit_before>//! Socket interface functions
//!
//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html)
use {NixError, NixResult, from_ffi};
use errno::Errno;
use features;
use fcntl::{fcntl, FD_CLOEXEC, O_NONBLOCK};
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
use libc::{c_void, c_int, socklen_t, size_t};
use std::{fmt, mem, ptr};
use std::os::unix::prelude::*;

mod addr;
mod consts;
mod ffi;
mod multicast;
pub mod sockopt;

/*
 *
 * ===== Re-exports =====
 *
 */

pub use self::addr::{
    AddressFamily,
    SockAddr,
    InetAddr,
    UnixAddr,
    IpAddr,
    Ipv4Addr,
    Ipv6Addr,
};
pub use libc::{
    in_addr,
    in6_addr,
    sockaddr,
    sockaddr_storage,
    sockaddr_in,
    sockaddr_in6,
    sockaddr_un,
    sa_family_t,
};

pub use self::multicast::{
    ip_mreq,
};
pub use self::consts::*;

#[derive(Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum SockType {
    Stream = consts::SOCK_STREAM,
    Datagram = consts::SOCK_DGRAM,
    SeqPacket = consts::SOCK_SEQPACKET,
    Raw = consts::SOCK_RAW,
    Rdm = consts::SOCK_RDM,
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
);

/// Create an endpoint for communication
///
/// [Further reading](http://man7.org/linux/man-pages/man2/socket.2.html)
pub fn socket(domain: AddressFamily, ty: SockType, flags: SockFlag) -> NixResult<Fd> {
    let mut ty = ty as c_int;
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain as c_int, ty, 0) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

/// Listen for connections on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/listen.2.html)
pub fn listen(sockfd: Fd, backlog: usize) -> NixResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

/// Bind a name to a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/bind.2.html)
pub fn bind(fd: Fd, addr: &SockAddr) -> NixResult<()> {
    let res = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::bind(fd, ptr, len)
    };

    from_ffi(res)
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
pub fn accept(sockfd: Fd) -> NixResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    Ok(res)
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(NixError::Sys(Errno::last()));
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

/// Initiate a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/connect.2.html)
pub fn connect(fd: Fd, addr: &SockAddr) -> NixResult<()> {
    let res = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::connect(fd, ptr, len)
    };

    from_ffi(res)
}

/// Receive data from a connectionless or connection-oriented socket. Returns
/// the number of bytes read and the socket address of the sender.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/recvmsg.2.html)
pub fn recvfrom(sockfd: Fd, buf: &mut [u8]) -> NixResult<(usize, SockAddr)> {
    unsafe {
        let addr: sockaddr_storage = mem::zeroed();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::recvfrom(
            sockfd,
            buf.as_ptr() as *mut c_void,
            buf.len() as size_t,
            0,
            mem::transmute(&addr),
            &mut len as *mut socklen_t);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
            .map(|addr| (ret as usize, addr))
    }
}

pub fn sendto(fd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> NixResult<usize> {
    let ret = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags, ptr, len)
    };

    if ret < 0 {
        Err(NixError::Sys(Errno::last()))
    } else {
        Ok(ret as usize)
    }
}

#[repr(C)]
#[derive(Copy, Debug)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

/*
 *
 * ===== Socket Options =====
 *
 */

/// The protocol level at which to get / set socket options. Used as an
/// argument to `getsockopt` and `setsockopt`.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
#[repr(i32)]
pub enum SockLevel {
    Socket = SOL_SOCKET,
    Tcp = IPPROTO_TCP,
    Ip = IPPROTO_IP,
    Ipv6 = IPPROTO_IPV6,
    Udp = IPPROTO_UDP,
}

/// Represents a socket option that can be accessed or set. Used as an argument
/// to `getsockopt` and `setsockopt`.
pub trait SockOpt : Copy + fmt::Debug {
    /// Type of `getsockopt` return value
    type Get;

    /// Type of value used to set the socket option. Used as the argument to
    /// `setsockopt`.
    type Set;

    #[doc(hidden)]
    fn get(&self, fd: Fd, level: c_int) -> NixResult<Self::Get>;

    #[doc(hidden)]
    fn set(&self, fd: Fd, level: c_int, val: Self::Set) -> NixResult<()>;
}

/// Get the current value for the requested socket option
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
pub fn getsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O) -> NixResult<O::Get> {
    opt.get(fd, level as c_int)
}

/// Sets the value for the requested socket option
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
pub fn setsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O, val: O::Set) -> NixResult<()> {
    opt.set(fd, level as c_int, val)
}

/// Get the address of the peer connected to the socket `fd`.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/getpeername.2.html)
pub fn getpeername(fd: Fd) -> NixResult<SockAddr> {
    unsafe {
        let addr: sockaddr_storage = mem::uninitialized();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::getpeername(fd, mem::transmute(&addr), &mut len);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
    }
}

/// Get the current address to which the socket `fd` is bound.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/getsockname.2.html)
pub fn getsockname(fd: Fd) -> NixResult<SockAddr> {
    unsafe {
        let addr: sockaddr_storage = mem::uninitialized();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::getsockname(fd, mem::transmute(&addr), &mut len);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
    }
}

unsafe fn sockaddr_storage_to_addr(
    addr: &sockaddr_storage,
    len: usize) -> NixResult<SockAddr> {

    match addr.ss_family as c_int {
        consts::AF_INET => {
            assert!(len as usize == mem::size_of::<sockaddr_in>());
            let ret = *(addr as *const _ as *const sockaddr_in);
            Ok(SockAddr::Inet(InetAddr::V4(ret)))
        }
        consts::AF_INET6 => {
            assert!(len as usize == mem::size_of::<sockaddr_in6>());
            Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6)))))
        }
        consts::AF_UNIX => {
            assert!(len as usize == mem::size_of::<sockaddr_un>());
            Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un))))
        }
        af => panic!("unexpected address family {}", af),
    }
}

#[test]
pub fn test_struct_sizes() {
    use nixtest;
    nixtest::assert_size_of::<sockaddr_storage>("sockaddr_storage");
}
<commit_msg>Derive FromPrimitive for SockType.<commit_after>//! Socket interface functions
//!
//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html)
use {NixError, NixResult, from_ffi};
use errno::Errno;
use features;
use fcntl::{fcntl, FD_CLOEXEC, O_NONBLOCK};
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
use libc::{c_void, c_int, socklen_t, size_t};
use std::{fmt, mem, ptr};
use std::os::unix::prelude::*;

mod addr;
mod consts;
mod ffi;
mod multicast;
pub mod sockopt;

/*
 *
 * ===== Re-exports =====
 *
 */

pub use self::addr::{
    AddressFamily,
    SockAddr,
    InetAddr,
    UnixAddr,
    IpAddr,
    Ipv4Addr,
    Ipv6Addr,
};
pub use libc::{
    in_addr,
    in6_addr,
    sockaddr,
    sockaddr_storage,
    sockaddr_in,
    sockaddr_in6,
    sockaddr_un,
    sa_family_t,
};

pub use self::multicast::{
    ip_mreq,
};
pub use self::consts::*;

#[derive(Copy, PartialEq, Eq, Debug, FromPrimitive)]
#[repr(i32)]
pub enum SockType {
    Stream = consts::SOCK_STREAM,
    Datagram = consts::SOCK_DGRAM,
    SeqPacket = consts::SOCK_SEQPACKET,
    Raw = consts::SOCK_RAW,
    Rdm = consts::SOCK_RDM,
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
);

/// Create an endpoint for communication
///
/// [Further reading](http://man7.org/linux/man-pages/man2/socket.2.html)
pub fn socket(domain: AddressFamily, ty: SockType, flags: SockFlag) -> NixResult<Fd> {
    let mut ty = ty as c_int;
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain as c_int, ty, 0) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

/// Listen for connections on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/listen.2.html)
pub fn listen(sockfd: Fd, backlog: usize) -> NixResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

/// Bind a name to a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/bind.2.html)
pub fn bind(fd: Fd, addr: &SockAddr) -> NixResult<()> {
    let res = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::bind(fd, ptr, len)
    };

    from_ffi(res)
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
pub fn accept(sockfd: Fd) -> NixResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    Ok(res)
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(NixError::Sys(Errno::last()));
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

/// Accept a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(NixError::Sys(Errno::last()));
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

/// Initiate a connection on a socket
///
/// [Further reading](http://man7.org/linux/man-pages/man2/connect.2.html)
pub fn connect(fd: Fd, addr: &SockAddr) -> NixResult<()> {
    let res = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::connect(fd, ptr, len)
    };

    from_ffi(res)
}

/// Receive data from a connectionless or connection-oriented socket. Returns
/// the number of bytes read and the socket address of the sender.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/recvmsg.2.html)
pub fn recvfrom(sockfd: Fd, buf: &mut [u8]) -> NixResult<(usize, SockAddr)> {
    unsafe {
        let addr: sockaddr_storage = mem::zeroed();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::recvfrom(
            sockfd,
            buf.as_ptr() as *mut c_void,
            buf.len() as size_t,
            0,
            mem::transmute(&addr),
            &mut len as *mut socklen_t);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
            .map(|addr| (ret as usize, addr))
    }
}

pub fn sendto(fd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> NixResult<usize> {
    let ret = unsafe {
        let (ptr, len) = addr.as_ffi_pair();
        ffi::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags, ptr, len)
    };

    if ret < 0 {
        Err(NixError::Sys(Errno::last()))
    } else {
        Ok(ret as usize)
    }
}

#[repr(C)]
#[derive(Copy, Debug)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

/*
 *
 * ===== Socket Options =====
 *
 */

/// The protocol level at which to get / set socket options. Used as an
/// argument to `getsockopt` and `setsockopt`.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
#[repr(i32)]
pub enum SockLevel {
    Socket = SOL_SOCKET,
    Tcp = IPPROTO_TCP,
    Ip = IPPROTO_IP,
    Ipv6 = IPPROTO_IPV6,
    Udp = IPPROTO_UDP,
}

/// Represents a socket option that can be accessed or set. Used as an argument
/// to `getsockopt` and `setsockopt`.
pub trait SockOpt : Copy + fmt::Debug {
    /// Type of `getsockopt` return value
    type Get;

    /// Type of value used to set the socket option. Used as the argument to
    /// `setsockopt`.
    type Set;

    #[doc(hidden)]
    fn get(&self, fd: Fd, level: c_int) -> NixResult<Self::Get>;

    #[doc(hidden)]
    fn set(&self, fd: Fd, level: c_int, val: Self::Set) -> NixResult<()>;
}

/// Get the current value for the requested socket option
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
pub fn getsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O) -> NixResult<O::Get> {
    opt.get(fd, level as c_int)
}

/// Sets the value for the requested socket option
///
/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html)
pub fn setsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O, val: O::Set) -> NixResult<()> {
    opt.set(fd, level as c_int, val)
}

/// Get the address of the peer connected to the socket `fd`.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/getpeername.2.html)
pub fn getpeername(fd: Fd) -> NixResult<SockAddr> {
    unsafe {
        let addr: sockaddr_storage = mem::uninitialized();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::getpeername(fd, mem::transmute(&addr), &mut len);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
    }
}

/// Get the current address to which the socket `fd` is bound.
///
/// [Further reading](http://man7.org/linux/man-pages/man2/getsockname.2.html)
pub fn getsockname(fd: Fd) -> NixResult<SockAddr> {
    unsafe {
        let addr: sockaddr_storage = mem::uninitialized();
        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;

        let ret = ffi::getsockname(fd, mem::transmute(&addr), &mut len);

        if ret < 0 {
            return Err(NixError::last());
        }

        sockaddr_storage_to_addr(&addr, len as usize)
    }
}

unsafe fn sockaddr_storage_to_addr(
    addr: &sockaddr_storage,
    len: usize) -> NixResult<SockAddr> {

    match addr.ss_family as c_int {
        consts::AF_INET => {
            assert!(len as usize == mem::size_of::<sockaddr_in>());
            let ret = *(addr as *const _ as *const sockaddr_in);
            Ok(SockAddr::Inet(InetAddr::V4(ret)))
        }
        consts::AF_INET6 => {
            assert!(len as usize == mem::size_of::<sockaddr_in6>());
            Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6)))))
        }
        consts::AF_UNIX => {
            assert!(len as usize == mem::size_of::<sockaddr_un>());
            Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un))))
        }
        af => panic!("unexpected address family {}", af),
    }
}

#[test]
pub fn test_struct_sizes() {
    use nixtest;
    nixtest::assert_size_of::<sockaddr_storage>("sockaddr_storage");
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Johannes Köster, Martin Larralde.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! One-way orf finder algorithm.
//! complexity: O(n).

//! # Example
//!
//! ```
//! use bio::seq_analysis::orf::NaiveFinder;
//! let start_codons = vec!(b"ATG");
//! let stop_codons  = vec!(b"TGA", b"TAG", b"TAA");
//! let min_len:usize = 50;
//! let finder = NaiveFinder::new(start_codons, stop_codons, min_len);
//!
//! let sequence = b"ACGGCTAGAAAAGGCTAGAAAA"
//!
//! for orf in finder.find_all(sequence) {
//!    ...do something...
//! }
//! ```

//! Right now the only way to check the reverse strand for orf is to use
//! the alphabet::dna::RevComp struct and to check for both sequences.
//! But that's not so performance friendly, as the reverse complementation and the orf research
//! could go on at the same time.

/// Finder algorithm
pub struct NaiveFinder <'a> {
    start_codons: Vec<&'a [u8; 3]>,
    stop_codons: Vec<&'a [u8; 3]>,
    min_len: usize,
}

impl<'a> NaiveFinder <'a> {
    /// Create a new instance for given genetic datas
    pub fn new (start_codons: Vec<&'a [u8; 3]>, stop_codons: Vec<&'a [u8; 3]>, min_len: usize) -> Self {
        NaiveFinder {
            start_codons: start_codons,
            stop_codons: stop_codons,
            min_len: min_len,
        }
    }
    /// Find all open reading frames in given sequence. Matches are returned as iterators over vectors
    pub fn find_all(&'a self, sequence: &'a [u8]) -> Matches {
        Matches::new(self, sequence)
    }
}

/// container struct for argument iteration
struct State {
    index: usize,
    length: usize,
    start_pos: [usize; 3],
    end_pos: [usize; 3],
    in_orf: [bool; 3]
}

impl State {
    /// Create new state
    pub fn new(length: usize) -> Self {
        State {
            index: 0,
            length: length,
            start_pos: [0, 0, 0],
            end_pos: [0, 0, 0],
            in_orf: [false, false, false],
        }
    }
}

/// Iterator yielding open reading frames as vector (start_pos, end_pos + 1, sequence)
pub struct Matches<'a> {
    finder: &'a NaiveFinder<'a>,
    sequence: &'a [u8],
    state: State,
}

impl<'a> Matches<'a> {
    pub fn new (finder: &'a NaiveFinder, sequence: &'a [u8]) -> Self {

        //let mut v = Vec::new();
        //v.extend_from_slice(sequence);

        Matches {
            finder: finder,
            sequence: sequence,
            state: State::new(sequence.len()),
        }
    }
}

impl<'a> Iterator for Matches<'a> {
    type Item = (usize, usize, &'a [u8]);

    fn next(&mut self) -> Option<(usize, usize, &'a [u8])> {

        let mut orf: Option<(usize, usize, &'a [u8])> = None;
        let mut codons: [ [u8; 3]; 3]; //

        while self.state.index < self.state.length - 3 {
            //codons array depending on reading frame
            codons = [
                [ self.sequence[self.state.index]  , self.sequence[self.state.index+1], self.sequence[self.state.index+2] ],
                [ self.sequence[self.state.index+1], self.sequence[self.state.index+2], self.sequence[self.state.index+3] ],
                [ self.sequence[self.state.index+2], self.sequence[self.state.index+3], self.sequence[self.state.index+4] ],
            ];
            //x is the shift of the reading frame
            for x in 0..3 as usize {

                if self.state.in_orf[x] {
                    if self.finder.stop_codons.contains(&&codons[x]) {
                        //exiting orf
                        self.state.end_pos[x] = self.state.index + x + 3;
                        self.state.in_orf[x] = false;
                        //slice the sequence to get the frame
                        let slice = &self.sequence[self.state.start_pos[x]..self.state.end_pos[x]];

                        // (!) still don't know if it's better to return slice or vec
                        // if vec is better, do:
                        //   let mut frame = Vec::new();
                        //   frame.extend_from_slice(slice);
                        // and return Some(.., frame) instead of Some(.., slice)

                        //check if orf length is enough
                        if slice.len() > self.finder.min_len {
                            orf = Some( (self.state.start_pos[x], self.state.end_pos[x], slice) );
                        }
                        self.state.start_pos[x] = 0;
                        self.state.end_pos[x] = 0
                    }
                } else {
                    if self.finder.start_codons.contains(&&codons[x]) {
                        //entering orf
                        self.state.start_pos[x] = self.state.index + x;
                        self.state.in_orf[x] = true;
                    }
                }

            }
            self.state.index += 3;
            if orf.is_some() {
                return orf;
            }
        }
        None
    }
}
<commit_msg>pulled latest rust-bio/rust-bio<commit_after>// Copyright 2014-2016 Johannes Köster, Martin Larralde.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! One-way orf finder algorithm.
//! complexity: O(n).

//! # Example
//!
//! ```
//! use bio::seq_analysis::orf::NaiveFinder;
//! let start_codons = vec!(b"ATG");
//! let stop_codons  = vec!(b"TGA", b"TAG", b"TAA");
//! let min_len:usize = 50;
//! let finder = NaiveFinder::new(start_codons, stop_codons, min_len);
//!
//! let sequence = b"ACGGCTAGAAAAGGCTAGAAAA"
//!
//! for orf in finder.find_all(sequence) {
//!    ...do something...
//! }
//! ```

//! Right now the only way to check the reverse strand for orf is to use
//! the alphabet::dna::RevComp struct and to check for both sequences.
//! But that's not so performance friendly, as the reverse complementation and the orf research
//! could go on at the same time.

const CODON_LENGTH:usize = 3;

/// Finder algorithm
pub struct NaiveFinder <'a> {
    start_codons: Vec<&'a [u8; 3]>,
    stop_codons: Vec<&'a [u8; 3]>,
    min_len: usize,
}

impl<'a> NaiveFinder <'a> {
    /// Create a new instance for given genetic datas
    pub fn new (start_codons: Vec<&'a [u8; 3]>, stop_codons: Vec<&'a [u8; 3]>, min_len: usize) -> Self {
        NaiveFinder {
            start_codons: start_codons,
            stop_codons: stop_codons,
            min_len: min_len,
        }
    }
    /// Find all open reading frames in given sequence. Matches are returned as iterators over vectors
    pub fn find_all(&'a self, sequence: &'a [u8]) -> Matches {
        Matches::new(self, sequence)
    }
}

/// container struct for argument iteration
struct State {
    index: usize,
    length: usize,
    start_pos: [usize; 3],
    end_pos: [usize; 3],
    in_orf: [bool; 3]
}

impl State {
    /// Create new state
    pub fn new(length: usize) -> Self {
        State {
            index: 0,
            length: length,
            start_pos: [0, 0, 0],
            end_pos: [0, 0, 0],
            in_orf: [false, false, false],
        }
    }
}

/// Iterator yielding open reading frames as vector (start_pos, end_pos + 1, sequence)
pub struct Matches<'a> {
    finder: &'a NaiveFinder<'a>,
    sequence: &'a [u8],
    state: State,
}

impl<'a> Matches<'a> {
    pub fn new (finder: &'a NaiveFinder, sequence: &'a [u8]) -> Self {

        //let mut v = Vec::new();
        //v.extend_from_slice(sequence);

        Matches {
            finder: finder,
            sequence: sequence,
            state: State::new(sequence.len()),
        }
    }
}

impl<'a> Matches <'a>{
    fn codon(&mut self, offset:usize) -> [u8; 3] {
        &self.sequence[self.state.index+offset..self.state.index+offset+CODON_LENGTH] as [u8;3]
    }
}

impl<'a> Iterator for Matches<'a> {
    type Item = (usize, usize, &'a [u8]);

    fn next(&mut self) -> Option<(usize, usize, &'a [u8])> {

        let mut orf: Option<(usize, usize, &'a [u8])> = None;
        let mut codons: [ [u8; 3]; 3]; //

        while self.state.index < self.state.length - CODON_LENGTH {
            //codons array depending on reading frame
            codons = [
                [ self.sequence[self.state.index]  , self.sequence[self.state.index+1], self.sequence[self.state.index+2] ],
                [ self.sequence[self.state.index+1], self.sequence[self.state.index+2], self.sequence[self.state.index+3] ],
                [ self.sequence[self.state.index+2], self.sequence[self.state.index+3], self.sequence[self.state.index+4] ],
            ];
            //x is the shift of the reading frame
            for x in 0..3 as usize {

                if self.state.in_orf[x] {
                    if self.finder.stop_codons.contains(&&codons[x]) {
                        //exiting orf
                        self.state.end_pos[x] = self.state.index + x + CODON_LENGTH;
                        self.state.in_orf[x] = false;
                        //slice the sequence to get the frame
                        let slice = &self.sequence[self.state.start_pos[x]..self.state.end_pos[x]];

                        // (!) still don't know if it's better to return slice or vec
                        // if vec is better, do:
                        //   let mut frame = Vec::new();
                        //   frame.extend_from_slice(slice);
                        // and return Some(.., frame) instead of Some(.., slice)

                        //check if orf length is enough
                        if slice.len() > self.finder.min_len {
                            orf = Some( (self.state.start_pos[x], self.state.end_pos[x], slice) );
                        }
                        self.state.start_pos[x] = 0;
                        self.state.end_pos[x] = 0
                    }
                } else {
                    if self.finder.start_codons.contains(&&codons[x]) {
                        //entering orf
                        self.state.start_pos[x] = self.state.index + x;
                        self.state.in_orf[x] = true;
                    }
                }

            }
            self.state.index += CODON_LENGTH;
            if orf.is_some() {
                return orf;
            }
        }
        None
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{DerefMut, Deref};

use toml::Value;

use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagtag::tag::Tag;
use libimagtag::tagable::Tagable;
use libimagtag::result::Result as TagResult;

use module_path::ModuleEntryPath;
use result::Result;
use error::NoteError as NE;
use error::NoteErrorKind as NEK;

pub struct Note<'a> {
    entry: FileLockEntry<'a>,
}

impl<'a> Note<'a> {

    pub fn new(store: &Store, name: String, text: String) -> Result<Note> {
        use std::ops::DerefMut;

        debug!("Creating new Note: '{}'", name);
        let fle = {
            let mut lockentry = store.create(ModuleEntryPath::new(name.clone()).into_storeid());
            if lockentry.is_err() {
                return Err(NE::new(NEK::StoreWriteError, Some(Box::new(lockentry.err().unwrap()))));
            }
            let mut lockentry = lockentry.unwrap();

            {
                let mut entry  = lockentry.deref_mut();
                let mut header = entry.get_header_mut();
                let setres = header.set("note", Value::Table(BTreeMap::new()));
                if setres.is_err() {
                    return Err(NE::new(NEK::StoreWriteError, Some(Box::new(setres.err().unwrap()))));
                }

                let setres = header.set("note.name", Value::String(name));
                if setres.is_err() {
                    return Err(NE::new(NEK::StoreWriteError, Some(Box::new(setres.err().unwrap()))));
                }
            }

            lockentry
        };

        Ok(Note { entry: fle })
    }

    pub fn set_name(&mut self, n: String) -> Result<()> {
        let mut header = self.entry.deref_mut().get_header_mut();
        header.set("note.name", Value::String(n))
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
            .map(|_| ())
    }

    pub fn get_name(&self) -> Result<String> {
        let mut header = self.entry.deref().get_header();
        match header.read("note.name") {
            Ok(Some(Value::String(s))) => Ok(String::from(s)),
            Ok(_)                => {
                let e = NE::new(NEK::HeaderTypeError, None);
                Err(NE::new(NEK::StoreReadError, Some(Box::new(e))))
            },
            Err(e) => Err(NE::new(NEK::StoreReadError, Some(Box::new(e))))
        }
    }

    pub fn set_text(&mut self, n: String) {
        *self.entry.deref_mut().get_content_mut() = n
    }

    pub fn get_text(&self) -> &String {
        self.entry.deref().get_content()
    }

    pub fn delete(store: &Store, name: String) -> Result<()> {
        store.delete(ModuleEntryPath::new(name).into_storeid())
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
    }

    pub fn retrieve(store: &Store, name: String) -> Result<Note> {
        store.retrieve(ModuleEntryPath::new(name).into_storeid())
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
            .map(|entry| Note { entry: entry })
    }

    pub fn all_notes(store: &Store) -> Result<NoteIterator> {
        store.retrieve_for_module("notes")
            .map(|iter| NoteIterator::new(store, iter))
            .map_err(|e| NE::new(NEK::StoreReadError, Some(Box::new(e))))
    }

}

impl<'a> Tagable for Note<'a> {

    fn get_tags(&self) -> TagResult<Vec<Tag>> {
        self.entry.deref().get_tags()
    }

    fn set_tags(&mut self, ts: Vec<Tag>) -> TagResult<()> {
        self.entry.deref_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> TagResult<()> {
        self.entry.deref_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> TagResult<()> {
        self.entry.deref_mut().remove_tag(t)
    }

    fn has_tag(&self, t: &Tag) -> TagResult<bool> {
        self.entry.deref().has_tag(t)
    }

    fn has_tags(&self, ts: &Vec<Tag>) -> TagResult<bool> {
        self.entry.deref().has_tags(ts)
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Note<'a>>;
}

impl<'a> FromStoreId for Note<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Note<'b>> {
        debug!("Loading note from storeid: '{:?}'", id);
        match store.retrieve(id) {
            Err(e)    => Err(NE::new(NEK::StoreReadError, Some(Box::new(e)))),
            Ok(entry) => Ok(Note { entry: entry }),
        }
    }

}

pub struct NoteIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> NoteIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> NoteIterator<'a> {
        NoteIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for NoteIterator<'a> {
    type Item = Result<Note<'a>>;

    fn next(&mut self) -> Option<Result<Note<'a>>> {
        self.iditer
            .next()
            .map(|id| Note::from_storeid(self.store, id))
    }

}

<commit_msg>lib: Add missing setting of text content<commit_after>use std::collections::BTreeMap;
use std::ops::{DerefMut, Deref};

use toml::Value;

use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagtag::tag::Tag;
use libimagtag::tagable::Tagable;
use libimagtag::result::Result as TagResult;

use module_path::ModuleEntryPath;
use result::Result;
use error::NoteError as NE;
use error::NoteErrorKind as NEK;

pub struct Note<'a> {
    entry: FileLockEntry<'a>,
}

impl<'a> Note<'a> {

    pub fn new(store: &Store, name: String, text: String) -> Result<Note> {
        use std::ops::DerefMut;

        debug!("Creating new Note: '{}'", name);
        let fle = {
            let mut lockentry = store.create(ModuleEntryPath::new(name.clone()).into_storeid());
            if lockentry.is_err() {
                return Err(NE::new(NEK::StoreWriteError, Some(Box::new(lockentry.err().unwrap()))));
            }
            let mut lockentry = lockentry.unwrap();

            {
                let mut entry  = lockentry.deref_mut();

                {
                    let mut header = entry.get_header_mut();
                    let setres = header.set("note", Value::Table(BTreeMap::new()));
                    if setres.is_err() {
                        let kind = NEK::StoreWriteError;
                        return Err(NE::new(kind, Some(Box::new(setres.err().unwrap()))));
                    }

                    let setres = header.set("note.name", Value::String(name));
                    if setres.is_err() {
                        let kind = NEK::StoreWriteError;
                        return Err(NE::new(kind, Some(Box::new(setres.err().unwrap()))));
                    }
                }

                *entry.get_content_mut() = text;
            }

            lockentry
        };

        Ok(Note { entry: fle })
    }

    pub fn set_name(&mut self, n: String) -> Result<()> {
        let mut header = self.entry.deref_mut().get_header_mut();
        header.set("note.name", Value::String(n))
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
            .map(|_| ())
    }

    pub fn get_name(&self) -> Result<String> {
        let mut header = self.entry.deref().get_header();
        match header.read("note.name") {
            Ok(Some(Value::String(s))) => Ok(String::from(s)),
            Ok(_)                => {
                let e = NE::new(NEK::HeaderTypeError, None);
                Err(NE::new(NEK::StoreReadError, Some(Box::new(e))))
            },
            Err(e) => Err(NE::new(NEK::StoreReadError, Some(Box::new(e))))
        }
    }

    pub fn set_text(&mut self, n: String) {
        *self.entry.deref_mut().get_content_mut() = n
    }

    pub fn get_text(&self) -> &String {
        self.entry.deref().get_content()
    }

    pub fn delete(store: &Store, name: String) -> Result<()> {
        store.delete(ModuleEntryPath::new(name).into_storeid())
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
    }

    pub fn retrieve(store: &Store, name: String) -> Result<Note> {
        store.retrieve(ModuleEntryPath::new(name).into_storeid())
            .map_err(|e| NE::new(NEK::StoreWriteError, Some(Box::new(e))))
            .map(|entry| Note { entry: entry })
    }

    pub fn all_notes(store: &Store) -> Result<NoteIterator> {
        store.retrieve_for_module("notes")
            .map(|iter| NoteIterator::new(store, iter))
            .map_err(|e| NE::new(NEK::StoreReadError, Some(Box::new(e))))
    }

}

impl<'a> Tagable for Note<'a> {

    fn get_tags(&self) -> TagResult<Vec<Tag>> {
        self.entry.deref().get_tags()
    }

    fn set_tags(&mut self, ts: Vec<Tag>) -> TagResult<()> {
        self.entry.deref_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> TagResult<()> {
        self.entry.deref_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> TagResult<()> {
        self.entry.deref_mut().remove_tag(t)
    }

    fn has_tag(&self, t: &Tag) -> TagResult<bool> {
        self.entry.deref().has_tag(t)
    }

    fn has_tags(&self, ts: &Vec<Tag>) -> TagResult<bool> {
        self.entry.deref().has_tags(ts)
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Note<'a>>;
}

impl<'a> FromStoreId for Note<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Note<'b>> {
        debug!("Loading note from storeid: '{:?}'", id);
        match store.retrieve(id) {
            Err(e)    => Err(NE::new(NEK::StoreReadError, Some(Box::new(e)))),
            Ok(entry) => Ok(Note { entry: entry }),
        }
    }

}

pub struct NoteIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> NoteIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> NoteIterator<'a> {
        NoteIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for NoteIterator<'a> {
    type Item = Result<Note<'a>>;

    fn next(&mut self) -> Option<Result<Note<'a>>> {
        self.iditer
            .next()
            .map(|id| Note::from_storeid(self.store, id))
    }

}

<|endoftext|>


<commit_before>//! Contains the binary logic of Ion.
mod designators;
mod prompt;
mod readln;
mod terminate;

use self::prompt::{prompt, prompt_fn};
use self::readln::readln;
use self::terminate::{terminate_quotes, terminate_script_quotes};
use super::{FlowLogic, JobControl, Shell, ShellHistory};
use super::flags::*;
use super::flow_control::Statement;
use super::status::*;
use liner::{Buffer, Context};
use smallvec::SmallVec;
use std::env;
use std::fs::File;
use std::io::{self, ErrorKind, Write};
use std::iter::{self, FromIterator};
use std::path::Path;
use std::process;

pub(crate) trait Binary {
    /// Launches the shell, parses arguments, and then diverges into one of the `execution`
    /// paths.
    fn main(self);
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32;
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
}

impl Binary for Shell {
    fn prompt(&mut self) -> String { prompt(self) }

    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }

    fn readln(&mut self) -> Option<String> { readln(self) }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
        terminate_script_quotes(self, lines)
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        terminate_quotes(self, command)
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();
            let _ = writeln!(stderr, "ion: -c requires an argument");
            self.exit(FAILURE);
        }

        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of arguments: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
            self.exit(FAILURE);
        }
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.get_var_or_empty("HISTFILE");
                        eprintln!("ion: failed to find history file {}: {}", history_filename, err);
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            context
        });

        self.evaluate_init_file();

        self.variables.set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command.replace("\\\n", "")) {
                        let cmd: &str = &designators::expand_designators(&self, command.trim());
                        self.on_command(&cmd);

                        if cmd.starts_with('~') {
                            if !cmd.ends_with('/')
                                && self.variables
                                    .tilde_expansion(cmd, &self.directory_stack)
                                    .map_or(false, |ref path| Path::new(path).is_dir())
                            {
                                self.save_command_in_history(&[cmd, "/"].concat());
                            } else {
                                self.save_command_in_history(cmd);
                            }
                            self.update_variables();
                            continue;
                        }

                        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
                            self.save_command_in_history(&[cmd, "/"].concat());
                        } else {
                            self.save_command_in_history(cmd);
                        }
                    } else {
                        self.flow_control.level = 0;
                        self.flow_control.current_if_mode = 0;
                        self.flow_control.current_statement = Statement::Default;
                    }
                }
                self.update_variables();
            } else {
                self.flow_control.level = 0;
                self.flow_control.current_if_mode = 0;
                self.flow_control.current_statement = Statement::Default;
            }
        }
    }

    fn main(mut self) {
        let mut args = env::args().skip(1);
        while let Some(path) = args.next() {
            match path.as_str() {
                "-n" => {
                    self.flags |= NO_EXEC;
                    continue;
                }
                "-c" => self.execute_arguments(args),
                "--version" => self.display_version(),
                _ => {
                    let mut array = SmallVec::from_iter(Some(path.clone().into()));
                    for arg in args {
                        array.push(arg.into());
                    }
                    self.variables.set_array("args", array);
                    if let Err(err) = self.execute_script(&path) {
                        eprintln!("ion: {}", err);
                    }
                }
            }

            self.wait_for_background();
            let previous_status = self.previous_status;
            self.exit(previous_status);
        }

        self.execute_interactive();
    }

    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }
}

fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    let mut res = Vec::new();
    let mut word_start = None;

    macro_rules! check_boundary {
        ($c:expr, $index:expr, $escaped:expr) => {{
            if let Some(start) = word_start {
                if $c == ' ' && !$escaped {
                    res.push((start, $index));
                    word_start = None;
                }
            } else {
                if $c != ' ' {
                    word_start = Some($index);
                }
            }
        }}
    }

    let mut iter = buf.chars().enumerate();
    while let Some((i, &c)) = iter.next() {
        match c {
            '\\' => {
                if let Some((_, &cnext)) = iter.next() {
                    // We use `i` in order to include the backslash as part of the word
                    check_boundary!(cnext, i, true);
                }
            }
            c => check_boundary!(c, i, false),
        }
    }
    if let Some(start) = word_start {
        // When start has been set, that means we have encountered a full word.
        res.push((start, buf.num_chars()));
    }
    res
}
<commit_msg>Refactor Work in Binary Module<commit_after>//! Contains the binary logic of Ion.
mod designators;
mod prompt;
mod readln;
mod terminate;

use self::prompt::{prompt, prompt_fn};
use self::readln::readln;
use self::terminate::{terminate_quotes, terminate_script_quotes};
use super::{FlowLogic, JobControl, Shell, ShellHistory};
use super::flags::*;
use super::flow_control::Statement;
use super::status::*;
use liner::{Buffer, Context};
use smallvec::SmallVec;
use std::env;
use std::fs::File;
use std::io::ErrorKind;
use std::iter::{self, FromIterator};
use std::path::Path;
use std::process;

pub(crate) trait Binary {
    /// Launches the shell, parses arguments, and then diverges into one of the `execution`
    /// paths.
    fn main(self);
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32;
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
    // Handles commands given by the REPL, and saves them to history.
    fn save_command(&mut self, command: &str);
    // Resets the flow control fields to their default values.
    fn reset_flow(&mut self);
}

impl Binary for Shell {
    fn prompt(&mut self) -> String { prompt(self) }

    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }

    fn readln(&mut self) -> Option<String> { readln(self) }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
        terminate_script_quotes(self, lines)
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        terminate_quotes(self, command)
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            eprintln!("ion: -c requires an argument");
            self.exit(FAILURE);
        }

        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of arguments: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
            self.exit(FAILURE);
        }
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.get_var_or_empty("HISTFILE");
                        eprintln!("ion: failed to find history file {}: {}", history_filename, err);
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            context
        });

        self.evaluate_init_file();

        self.variables.set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command.replace("\\\n", "")) {
                        let cmd: &str = &designators::expand_designators(&self, command.trim());
                        self.on_command(&cmd);
                        self.save_command(&cmd);
                    } else {
                        self.reset_flow();
                    }
                }
                self.update_variables();
            } else {
                self.reset_flow();
            }
        }
    }

    fn reset_flow(&mut self) {
        self.flow_control.level = 0;
        self.flow_control.current_if_mode = 0;
        self.flow_control.current_statement = Statement::Default;
    }

    fn save_command(&mut self, cmd: &str) {
        if cmd.starts_with('~') {
            if !cmd.ends_with('/')
                && self.variables
                    .tilde_expansion(cmd, &self.directory_stack)
                    .map_or(false, |ref path| Path::new(path).is_dir())
            {
                self.save_command_in_history(&[cmd, "/"].concat());
            } else {
                self.save_command_in_history(cmd);
            }
            self.update_variables();
            return;
        }

        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
            self.save_command_in_history(&[cmd, "/"].concat());
        } else {
            self.save_command_in_history(cmd);
        }
    }

    fn main(mut self) {
        let mut args = env::args().skip(1);
        while let Some(path) = args.next() {
            match path.as_str() {
                "-n" => {
                    self.flags |= NO_EXEC;
                    continue;
                }
                "-c" => self.execute_arguments(args),
                "--version" => self.display_version(),
                _ => {
                    let mut array = SmallVec::from_iter(Some(path.clone().into()));
                    for arg in args {
                        array.push(arg.into());
                    }
                    self.variables.set_array("args", array);
                    if let Err(err) = self.execute_script(&path) {
                        eprintln!("ion: {}", err);
                    }
                }
            }

            self.wait_for_background();
            let previous_status = self.previous_status;
            self.exit(previous_status);
        }

        self.execute_interactive();
    }

    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }
}

// TODO: Convert this into an iterator to eliminate heap allocations.
fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    let mut res = Vec::new();
    let mut word_start = None;

    macro_rules! check_boundary {
        ($c:expr, $index:expr, $escaped:expr) => {{
            if let Some(start) = word_start {
                if $c == ' ' && !$escaped {
                    res.push((start, $index));
                    word_start = None;
                }
            } else {
                if $c != ' ' {
                    word_start = Some($index);
                }
            }
        }}
    }

    let mut iter = buf.chars().enumerate();
    while let Some((i, &c)) = iter.next() {
        match c {
            '\\' => {
                if let Some((_, &cnext)) = iter.next() {
                    // We use `i` in order to include the backslash as part of the word
                    check_boundary!(cnext, i, true);
                }
            }
            c => check_boundary!(c, i, false),
        }
    }
    if let Some(start) = word_start {
        // When start has been set, that means we have encountered a full word.
        res.push((start, buf.num_chars()));
    }
    res
}
<|endoftext|>


<commit_before>//!

use libc::*;
use std::ffi::CString;

pub enum CreateMode {
    CreateOnly,
    OpenOrCreate,
    OpenOnly,
}

pub enum AccessMode {
    ReadOnly,
    ReadWrite,
}

pub enum Permissions {
    Default,
    // TODO
}

pub struct Handle {
    shm_fd: c_int,
    name: CString,
    access_mode: AccessMode,
}

impl Handle {
    #[cfg(not(windows))]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => O_CREAT | O_EXCL,
            CreateMode::OpenOrCreate => O_CREAT,
            CreateMode::OpenOnly => 0,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => O_RDONLY,
            AccessMode::ReadWrite => O_RDWR,
        };
        let perm = match permissions {
            Permissions::Default => S_IRWXG,
            // TODO
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe { shm_open(cstr.as_ptr(), cmode | amode as c_int, perm as c_uint) };
        match fd {
            -1 => Err(format!("Unable to open/create shared memory object: {}", name)),
            _ => Ok(Handle {shm_fd: fd, name: cstr, access_mode: access_mode}),
        }
    }

    #[cfg(not(windows))]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { shm_unlink(cstr.as_ptr()) } {
            -1 => false,
            _ => true,
        }
    }

    #[cfg(windows)]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        // TODO
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        Ok(Handle {shm_fd: 0, name: cstr, access_mode: access_mode})
    }

    #[cfg(windows)]
    pub fn remove(name: &str) -> bool {
        // TODO
        true
    }

    pub fn name(&self) -> String {
        self.name.to_string_lossy().into_owned()
    }

    pub fn native_handle(&self) -> c_int {
        self.shm_fd
    }

    pub fn access_mode(&self) -> &AccessMode {
        &self.access_mode
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe { close(self.shm_fd) };
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn breath_test_handle() {
        use super::*;
        let name = "handleBreathTest";
        {
            let handle = Handle::new(name, CreateMode::CreateOnly, AccessMode::ReadWrite, Permissions::Default).unwrap();
            assert_eq!(handle.name(), name);
            match handle.access_mode() {
                &AccessMode::ReadWrite => {},
                _ => assert!(false, "wrong access mode"),
            };
            assert!(handle.native_handle() >= 0);
        }
        let removed = Handle::remove(name);
        assert!(removed);
    }
}
<commit_msg>more permissions<commit_after>//!

use libc::*;
use std::ffi::CString;

pub enum CreateMode {
    CreateOnly,
    OpenOrCreate,
    OpenOnly,
}

pub enum AccessMode {
    ReadOnly,
    ReadWrite,
}

pub enum Permissions {
    User,
    Group,
    Others
}

pub struct Handle {
    shm_fd: c_int,
    name: CString,
    access_mode: AccessMode,
}

impl Handle {
    #[cfg(not(windows))]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        let cmode = match create_mode {
            CreateMode::CreateOnly => O_CREAT | O_EXCL,
            CreateMode::OpenOrCreate => O_CREAT,
            CreateMode::OpenOnly => 0,
        };
        let amode = match access_mode {
            AccessMode::ReadOnly => O_RDONLY,
            AccessMode::ReadWrite => O_RDWR,
        };
        let perm = match permissions {
            Permissions::User => S_IRWXU,
            Permissions::Group => S_IRWXG,
            Permissions::Others => S_IRWXO,
        };
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        let fd = unsafe { shm_open(cstr.as_ptr(), cmode | amode as c_int, perm as c_uint) };
        match fd {
            -1 => Err(format!("Unable to open/create shared memory object: {}", name)),
            _ => Ok(Handle {shm_fd: fd, name: cstr, access_mode: access_mode}),
        }
    }

    #[cfg(not(windows))]
    pub fn remove(name: &str) -> bool {
        let cstr = match CString::new(name) {
            Err(_) => return false,
            Ok(val) => val,
        };
        match unsafe { shm_unlink(cstr.as_ptr()) } {
            -1 => false,
            _ => true,
        }
    }

    #[cfg(windows)]
    #[allow(unused)]
    pub fn new(name: &str, create_mode: CreateMode, access_mode: AccessMode, permissions: Permissions) -> Result<Handle, String> {
        // TODO
        let cstr = match CString::new(name) {
            Err(_) => return Err(format!("Unable to convert to CString: {}", name)),
            Ok(val) => val,
        };
        Ok(Handle {shm_fd: 0, name: cstr, access_mode: access_mode})
    }

    #[cfg(windows)]
    #[allow(unused)]
    pub fn remove(name: &str) -> bool {
        // TODO
        true
    }

    pub fn name(&self) -> String {
        self.name.to_string_lossy().into_owned()
    }

    pub fn native_handle(&self) -> c_int {
        self.shm_fd
    }

    pub fn access_mode(&self) -> &AccessMode {
        &self.access_mode
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe { close(self.shm_fd) };
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn breath_test_handle() {
        use super::*;
        let name = "handleBreathTest";
        {
            let handle = Handle::new(name, CreateMode::CreateOnly, AccessMode::ReadWrite, Permissions::User).unwrap();
            assert_eq!(handle.name(), name);
            match handle.access_mode() {
                &AccessMode::ReadWrite => {},
                _ => assert!(false, "wrong access mode"),
            };
            assert!(handle.native_handle() >= 0);
        }
        let removed = Handle::remove(name);
        assert!(removed);
    }
}
<|endoftext|>