<commit_before>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::BTreeMap;
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::RwLock;

use context;
use syscall::error::*;
use syscall::scheme::Scheme;
use scheme::{self, SchemeNamespace, SchemeId};
use scheme::user::{UserInner, UserScheme};

#[derive(Clone)]
enum UserOrListHandle {
    User(Arc<UserInner>),
    List(AtomicUsize)
}

pub struct RootScheme {
    scheme_ns: SchemeNamespace,
    scheme_id: SchemeId,
    next_id: AtomicUsize,
    handles: RwLock<BTreeMap<usize, UserOrListHandle>>,
}

impl RootScheme {
    pub fn new(scheme_ns: SchemeNamespace, scheme_id: SchemeId) -> RootScheme {
        RootScheme {
            scheme_ns: scheme_ns,
            scheme_id: scheme_id,
            next_id: AtomicUsize::new(0),
            handles: RwLock::new(BTreeMap::new())
        }
    }
}

impl Scheme for RootScheme {
    fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
        use syscall::*;
        if uid == 0 {
            if flags & O_DIRECTORY = O_DIRECTORY {
                if flags & O_ACCMODE != O_RDONLY {
                    return Err(Error::new(EACCES));
                }
                let id = self.next_id.fetch_add(1, Ordering::SeqCst);
                self.ls_handles.write().insert(id, UserOrListHandle::List(0));
                return Ok(id);
            }

            let context = {
                let contexts = context::contexts();
                let context = contexts.current().ok_or(Error::new(ESRCH))?;
                Arc::downgrade(&context)
            };

            let id = self.next_id.fetch_add(1, Ordering::SeqCst);

            let inner = {
                let path_box = path.to_vec().into_boxed_slice();
                let mut schemes = scheme::schemes_mut();
                let inner = Arc::new(UserInner::new(self.scheme_id, id, path_box.clone(), flags, context));
                schemes.insert(self.scheme_ns, path_box, |scheme_id| {
                    inner.scheme_id.store(scheme_id, Ordering::SeqCst);
                    Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))
                })?;
                inner
            };

            self.handles.write().insert(id, UserOrListHandler::User(inner));

            Ok(id)
        } else {
            Err(Error::new(EACCES))
        }
    }

    fn dup(&self, file: usize, _buf: &[u8]) -> Result<usize> {
        let mut handles = self.handles.write();
        let inner = {
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        handles.insert(id, inner);

        Ok(id)
    }

    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        match &*inner {
            UserOrListInner::User(ref inner) => inner.read(buf),
            UserOrListInner::List(ref num) => {
                let scheme_ns = {
                    let contexts = context::contexts();
                    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                    let context = context_lock.read();
                    context.ens
                };

                let schemes = scheme::schemes();

                let scheme_name = schemes.iterName(scheme_ns).nth(num.load(Ordering::SeqCst)).0.as_bytes();

                let mut i = 0;
                while i < buf.len() && i < scheme_name.len() {
                    buf[i] = scheme_name[i];
                    i += 1;
                }

                num.fetch_add(1, Ordering::SeqCst)

                Ok(i)
            }
        }
    }

    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        match &*inner {
            UserOrListInner::User(ref inner) => inner.write(buf),
            UserOrListInner::List(_) => Err(Error::new(::syscall::EBADF))
        }
    }

    fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fevent(flags)
    }

    fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let mut i = 0;
        let scheme_path = b":";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }

        let mut j = 0;
        while i < buf.len() && j < inner.name.len() {
            buf[i] = inner.name[j];
            i += 1;
            j += 1;
        }

        Ok(i)
    }

    fn fsync(&self, file: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fsync()
    }

    fn close(&self, file: usize) -> Result<usize> {
        self.handles.write().remove(&file).ok_or(Error::new(EBADF)).and(Ok(0))
    }
}
<commit_msg>Remove UserOrListHandle<commit_after>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::BTreeMap;
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::RwLock;

use context;
use syscall::error::*;
use syscall::scheme::Scheme;
use scheme::{self, SchemeNamespace, SchemeId};
use scheme::user::{UserInner, UserScheme};

pub struct RootScheme {
    scheme_ns: SchemeNamespace,
    scheme_id: SchemeId,
    next_id: AtomicUsize,
    handles: RwLock<BTreeMap<usize, Arc<UserInner>>>,
    ls_handles: RwLock<BTreeMap<usize, AtomicUsize>>,
}

impl RootScheme {
    pub fn new(scheme_ns: SchemeNamespace, scheme_id: SchemeId) -> RootScheme {
        RootScheme {
            scheme_ns: scheme_ns,
            scheme_id: scheme_id,
            next_id: AtomicUsize::new(0),
            handles: RwLock::new(BTreeMap::new()),
            ls_handles: RwLock::new(BTreeMap::new()),
        }
    }
}

impl Scheme for RootScheme {
    fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
        use syscall::syscall::*;
        if uid == 0 {
            if flags & O_DIRECTORY == O_DIRECTORY {
                if flags & O_ACCMODE != O_RDONLY {
                    return Err(Error::new(EACCES));
                }
                let id = self.next_id.fetch_add(1, Ordering::SeqCst);
                self.ls_handles.write().insert(id, AtomicUsize::new(0));
                return Ok(id);
            }

            let context = {
                let contexts = context::contexts();
                let context = contexts.current().ok_or(Error::new(ESRCH))?;
                Arc::downgrade(&context)
            };

            let id = self.next_id.fetch_add(1, Ordering::SeqCst);

            let inner = {
                use scheme;
                let path_box = path.to_vec().into_boxed_slice();
                let mut schemes = scheme::schemes_mut();
                let inner = Arc::new(UserInner::new(self.scheme_id, id, path_box.clone(), flags, context));
                schemes.insert(self.scheme_ns, path_box, |scheme_id| {
                    inner.scheme_id.store(scheme_id, Ordering::SeqCst);
                    Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))
                })?;
                inner
            };

            self.handles.write().insert(id, inner);

            Ok(id)
        } else {
            Err(Error::new(EACCES))
        }
    }

    fn dup(&self, file: usize, _buf: &[u8]) -> Result<usize> {
        let mut handles = self.handles.write();
        let inner = {
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        handles.insert(id, inner);

        Ok(id)
    }

    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        match &*inner {
            UserOrListInner::User(ref inner) => inner.read(buf),
            UserOrListInner::List(ref num) => {
                let scheme_ns = {
                    let contexts = context::contexts();
                    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
                    let context = context_lock.read();
                    context.ens
                };

                let schemes = scheme::schemes();

                let scheme_name = schemes.iterName(scheme_ns).nth(num.load(Ordering::SeqCst)).0.as_bytes();

                let mut i = 0;
                while i < buf.len() && i < scheme_name.len() {
                    buf[i] = scheme_name[i];
                    i += 1;
                }

                num.fetch_add(1, Ordering::SeqCst);

                Ok(i)
            }
        }
    }

    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.write(buf)
    }

    fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fevent(flags)
    }

    fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        let mut i = 0;
        let scheme_path = b":";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }

        let mut j = 0;
        while i < buf.len() && j < inner.name.len() {
            buf[i] = inner.name[j];
            i += 1;
            j += 1;
        }

        Ok(i)
    }

    fn fsync(&self, file: usize) -> Result<usize> {
        let inner = {
            let handles = self.handles.read();
            let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
            inner.clone()
        };

        inner.fsync()
    }

    fn close(&self, file: usize) -> Result<usize> {
        self.handles.write().remove(&file).ok_or(Error::new(EBADF)).and(Ok(0))
    }
}
<|endoftext|>


<commit_before>//! Haptic Functions
use sys::haptic as ll;
use sys::joystick as sys_joystick;

use HapticSubsystem;
use common::IntegerOrSdlError;
use get_error;

impl HapticSubsystem {
    /// Attempt to open the joystick at number `id` and return it.
    pub fn open_from_joystick_id(&self, joystick_index: i32) -> Result<Haptic, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;

        let haptic = unsafe {
            let joystick = sys_joystick::SDL_JoystickOpen(joystick_index);
            ll::SDL_HapticOpenFromJoystick(joystick)
        };

        if haptic.is_null() {
            Err(SdlError(get_error()))
        } else {
            unsafe { ll::SDL_HapticRumbleInit(haptic) };
            Ok(Haptic {
                subsystem: self.clone(),
                raw: haptic,
            })
        }
    }
}

/// Wrapper around the SDL_Haptic object
pub struct Haptic {
    subsystem: HapticSubsystem,
    raw: *mut ll::SDL_Haptic,
}

impl Haptic {
    #[inline]
    pub fn subsystem(&self) -> &HapticSubsystem { &self.subsystem }

    /// Run a simple rumble effect on the haptic device.
    pub fn rumble_play(&mut self, strength: f32, duration: u32) {
        unsafe { ll::SDL_HapticRumblePlay(self.raw, strength, duration) };
    }

    /// Stop the simple rumble on the haptic device.
    pub fn rumble_stop(&mut self) {
        unsafe { ll::SDL_HapticRumbleStop(self.raw) };
    }
}

impl Drop for Haptic {
    fn drop(&mut self) {
        unsafe { ll::SDL_HapticClose(self.raw) }
    }
}
<commit_msg>Update haptic to reflect that it uses joystick idx<commit_after>//! Haptic Functions
use sys::haptic as ll;
use sys::joystick as sys_joystick;

use HapticSubsystem;
use common::IntegerOrSdlError;
use get_error;

impl HapticSubsystem {
    /// Attempt to open the joystick at index `joystick_index` and return its haptic device.
    pub fn open_from_joystick_id(&self, joystick_index: u32) -> Result<Haptic, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;

        let haptic = unsafe {
            let joystick = sys_joystick::SDL_JoystickOpen(joystick_index);
            ll::SDL_HapticOpenFromJoystick(joystick)
        };

        if haptic.is_null() {
            Err(SdlError(get_error()))
        } else {
            unsafe { ll::SDL_HapticRumbleInit(haptic) };
            Ok(Haptic {
                subsystem: self.clone(),
                raw: haptic,
            })
        }
    }
}

/// Wrapper around the SDL_Haptic object
pub struct Haptic {
    subsystem: HapticSubsystem,
    raw: *mut ll::SDL_Haptic,
}

impl Haptic {
    #[inline]
    pub fn subsystem(&self) -> &HapticSubsystem { &self.subsystem }

    /// Run a simple rumble effect on the haptic device.
    pub fn rumble_play(&mut self, strength: f32, duration: u32) {
        unsafe { ll::SDL_HapticRumblePlay(self.raw, strength, duration) };
    }

    /// Stop the simple rumble on the haptic device.
    pub fn rumble_stop(&mut self) {
        unsafe { ll::SDL_HapticRumbleStop(self.raw) };
    }
}

impl Drop for Haptic {
    fn drop(&mut self) {
        unsafe { ll::SDL_HapticClose(self.raw) }
    }
}
<|endoftext|>


<commit_before>macro_rules! impl_raw_accessors(
    ($(($t:ty, $raw:ty)),+) => (
        $(
        impl $t {
            #[inline]
            pub fn raw(&self) -> $raw { self.raw }
        }
        )+
    )
);

macro_rules! impl_owned_accessors(
    ($(($t:ty, $owned:ident)),+) => (
        $(
        impl $t {
            #[inline]
            pub fn $owned(&self) -> bool { self.$owned }
        }
        )+
    )
);

macro_rules! impl_raw_constructor(
    ($(($t:ty, $te:ident ($($r:ident:$rt:ty),+))),+) => (
        $(
        impl $t {
            #[inline]
            pub unsafe fn from_ll($($r:$rt),+) -> $t {
                $te { $($r: $r),+ }
            }
        }
        )+
    )
);
<commit_msg>Mark raw() and owned() accessors as unsafe.<commit_after>macro_rules! impl_raw_accessors(
    ($(($t:ty, $raw:ty)),+) => (
        $(
        impl $t {
            #[inline]
            pub unsafe fn raw(&self) -> $raw { self.raw }
        }
        )+
    )
);

macro_rules! impl_owned_accessors(
    ($(($t:ty, $owned:ident)),+) => (
        $(
        impl $t {
            #[inline]
            pub unsafe fn $owned(&self) -> bool { self.$owned }
        }
        )+
    )
);

macro_rules! impl_raw_constructor(
    ($(($t:ty, $te:ident ($($r:ident:$rt:ty),+))),+) => (
        $(
        impl $t {
            #[inline]
            pub unsafe fn from_ll($($r:$rt),+) -> $t {
                $te { $($r: $r),+ }
            }
        }
        )+
    )
);
<|endoftext|>


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

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod sink;

use std::convert::TryInto;

use log::{error, info};

use crate::action;
use crate::message;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError, MissingFieldError};
use self::response::{Response, Status};
pub use self::sink::{Sink};

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Object associating a session with particular action request.
///
/// This is just a convenience type used to avoid threading large numbers of
/// parameters through different function calls.
///
/// Note that the payload can be quite large object, so it should not be stored
/// with the session itself. The payload should be consumed as soon as the
/// request is dispatched to a particular handler and parsed to a concrete type.
pub struct Task<'s, S: Session> {
    /// A session that this task should be run on.
    pub session: &'s mut S,
    /// Serialized data of the action request.
    pub payload: Payload,
}

impl<'s, S: Session> Task<'s, S> {

    /// Executes the task with a particular action handler.
    pub fn execute<R, H>(self, handler: H) -> Result<()>
    where
        R: action::Request,
        H: FnOnce(&mut S, R) -> Result<()>,
    {
        let request = self.payload.parse()?;
        handler(self.session, request)
    }
}

/// Processes given message, handling all errors.
///
/// This function takes a message from the GRR server and interprets it as an
/// action request. It creates a new session object and dispatches the request
/// to an appropriate action handler. Once the action finishes, the function
/// will send a final status message, informing the server about a success
/// or a failure.
///
/// Note that if action execution fails, this function deals with all the errors
/// by sending appropriate information to the server (if possible), logging them
/// and failing hard if a critical error (e.g. communication failure) occurred.
pub fn handle<M>(message: M)
where
    M: TryInto<Demand, Error=ParseError>,
{
    let demand = match message.try_into() {
        Ok(demand) => {
            info!("requested to execute the '{}' action", demand.action);
            demand
        }
        Err(error) => {
            error!("failed to parse the message: {}", error);
            return;
        }
    };

    let mut session = Action::from_demand(&demand);

    let result = action::dispatch(&demand.action, Task {
        session: &mut session,
        payload: demand.payload,
    });

    if let Err(ref error) = result {
        error!("failed to execute the '{}' action: {}", demand.action, error);
    } else {
        info!("finished executing the '{}' action", demand.action);
    }

    let message = match session.status(result).try_into() {
        Ok(message) => message,
        Err(error) => {
            // If we cannot encode the final status message, there is nothing
            // we can do to notify the server, as status is responsible for
            // reporting errors. We can only log the error and carry on.
            error!("failed to encode status message: {}", error);
            return;
        }
    };

    message::send(message);
}

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where R: action::Response;

    /// Sends a message to a particular sink.
    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where R: action::Response;

    /// Sends a heartbeat signal to the Fleetspeak process.
    fn heartbeat(&mut self) {
        // TODO: Create a real implementation.
    }
}

/// A session type for unrequested action executions.
///
/// Certain kind of actions are executed not only when a server flow decides to
/// do so, but also upon particular kind of events (e.g. the agent's startup).
/// In such cases, when one needs to trigger action execution manually, ad-hoc
/// sessions should be used.
pub struct Adhoc;

impl Session for Adhoc {

    // TODO: Session trait should be probably split into two traits and then
    // make the actions that do not care about the `reply` method implement the
    // simpler one.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where
        R: action::Response,
    {
        error!("attempted to reply to an ad-hoc session, dropping response");
        drop(response);

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// A session type for ordinary action requests.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct Action {
    header: Header,
    next_response_id: u64,
}

impl Action {

    /// Constructs a new session for the given `demand` object.
    pub fn from_demand(demand: &Demand) -> Action {
        // Response identifiers that GRR agents use start at 1. Unfortunately,
        // the server uses this assumption (to determine the number of expected
        // responses when status message is received), so we have to follow this
        // behaviour in RRG as well.
        Action {
            header: demand.header.clone(),
            next_response_id: 1,
        }
    }

    /// Wraps an action response to a session-specific response.
    fn wrap<R>(&self, response: R) -> Response<R>
    where
        R: action::Response
    {
        Response {
            session_id: self.header.session_id.clone(),
            request_id: Some(self.header.request_id),
            response_id: Some(self.next_response_id),
            data: response,
        }
    }

    /// Wraps an action result to a session-specific status response.
    ///
    /// Note that this method consumes the session. The reason for this is that
    /// status response should be obtained as the very last response and no
    /// further replies should be possible after that.
    fn status(self, result: Result<()>) -> Status {
        Status {
            session_id: self.header.session_id,
            request_id: self.header.request_id,
            response_id: self.next_response_id,
            result: result,
        }
    }
}

impl Session for Action {

    fn reply<R: action::Response>(&mut self, response: R) -> Result<()> {
        send(self.wrap(response))?;
        self.next_response_id += 1;

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// Sends a session response to the server.
///
/// Note that this function is not exposed on purpose. Actions should send
/// responses through session objects which introduce a layer of safety. `send`
/// is a low-level utility supposed to be used internally.
fn send<R>(response: Response<R>) -> Result<()>
where
    R: action::Response,
{
    let message = response.try_into()?;
    message::send(message);

    Ok(())
}
<commit_msg>Require static lifetime for session responses.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod sink;

use std::convert::TryInto;

use log::{error, info};

use crate::action;
use crate::message;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError, MissingFieldError};
use self::response::{Response, Status};
pub use self::sink::{Sink};

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Object associating a session with particular action request.
///
/// This is just a convenience type used to avoid threading large numbers of
/// parameters through different function calls.
///
/// Note that the payload can be quite large object, so it should not be stored
/// with the session itself. The payload should be consumed as soon as the
/// request is dispatched to a particular handler and parsed to a concrete type.
pub struct Task<'s, S: Session> {
    /// A session that this task should be run on.
    pub session: &'s mut S,
    /// Serialized data of the action request.
    pub payload: Payload,
}

impl<'s, S: Session> Task<'s, S> {

    /// Executes the task with a particular action handler.
    pub fn execute<R, H>(self, handler: H) -> Result<()>
    where
        R: action::Request,
        H: FnOnce(&mut S, R) -> Result<()>,
    {
        let request = self.payload.parse()?;
        handler(self.session, request)
    }
}

/// Processes given message, handling all errors.
///
/// This function takes a message from the GRR server and interprets it as an
/// action request. It creates a new session object and dispatches the request
/// to an appropriate action handler. Once the action finishes, the function
/// will send a final status message, informing the server about a success
/// or a failure.
///
/// Note that if action execution fails, this function deals with all the errors
/// by sending appropriate information to the server (if possible), logging them
/// and failing hard if a critical error (e.g. communication failure) occurred.
pub fn handle<M>(message: M)
where
    M: TryInto<Demand, Error=ParseError>,
{
    let demand = match message.try_into() {
        Ok(demand) => {
            info!("requested to execute the '{}' action", demand.action);
            demand
        }
        Err(error) => {
            error!("failed to parse the message: {}", error);
            return;
        }
    };

    let mut session = Action::from_demand(&demand);

    let result = action::dispatch(&demand.action, Task {
        session: &mut session,
        payload: demand.payload,
    });

    if let Err(ref error) = result {
        error!("failed to execute the '{}' action: {}", demand.action, error);
    } else {
        info!("finished executing the '{}' action", demand.action);
    }

    let message = match session.status(result).try_into() {
        Ok(message) => message,
        Err(error) => {
            // If we cannot encode the final status message, there is nothing
            // we can do to notify the server, as status is responsible for
            // reporting errors. We can only log the error and carry on.
            error!("failed to encode status message: {}", error);
            return;
        }
    };

    message::send(message);
}

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where R: action::Response + 'static;

    /// Sends a message to a particular sink.
    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where R: action::Response + 'static;

    /// Sends a heartbeat signal to the Fleetspeak process.
    fn heartbeat(&mut self) {
        // TODO: Create a real implementation.
    }
}

/// A session type for unrequested action executions.
///
/// Certain kind of actions are executed not only when a server flow decides to
/// do so, but also upon particular kind of events (e.g. the agent's startup).
/// In such cases, when one needs to trigger action execution manually, ad-hoc
/// sessions should be used.
pub struct Adhoc;

impl Session for Adhoc {

    // TODO: Session trait should be probably split into two traits and then
    // make the actions that do not care about the `reply` method implement the
    // simpler one.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where
        R: action::Response,
    {
        error!("attempted to reply to an ad-hoc session, dropping response");
        drop(response);

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// A session type for ordinary action requests.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct Action {
    header: Header,
    next_response_id: u64,
}

impl Action {

    /// Constructs a new session for the given `demand` object.
    pub fn from_demand(demand: &Demand) -> Action {
        // Response identifiers that GRR agents use start at 1. Unfortunately,
        // the server uses this assumption (to determine the number of expected
        // responses when status message is received), so we have to follow this
        // behaviour in RRG as well.
        Action {
            header: demand.header.clone(),
            next_response_id: 1,
        }
    }

    /// Wraps an action response to a session-specific response.
    fn wrap<R>(&self, response: R) -> Response<R>
    where
        R: action::Response
    {
        Response {
            session_id: self.header.session_id.clone(),
            request_id: Some(self.header.request_id),
            response_id: Some(self.next_response_id),
            data: response,
        }
    }

    /// Wraps an action result to a session-specific status response.
    ///
    /// Note that this method consumes the session. The reason for this is that
    /// status response should be obtained as the very last response and no
    /// further replies should be possible after that.
    fn status(self, result: Result<()>) -> Status {
        Status {
            session_id: self.header.session_id,
            request_id: self.header.request_id,
            response_id: self.next_response_id,
            result: result,
        }
    }
}

impl Session for Action {

    fn reply<R: action::Response>(&mut self, response: R) -> Result<()> {
        send(self.wrap(response))?;
        self.next_response_id += 1;

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// Sends a session response to the server.
///
/// Note that this function is not exposed on purpose. Actions should send
/// responses through session objects which introduce a layer of safety. `send`
/// is a low-level utility supposed to be used internally.
fn send<R>(response: Response<R>) -> Result<()>
where
    R: action::Response,
{
    let message = response.try_into()?;
    message::send(message);

    Ok(())
}
<|endoftext|>


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

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

extern crate getopts;
extern crate unindent;
extern crate byteorder;

#[macro_use]
extern crate uucore;

mod multifilereader;
mod byteorder_io;
mod formatteriteminfo;
mod prn_int;
mod prn_char;
mod prn_float;

use std::cmp;
use std::io::Write;
use unindent::*;
use byteorder_io::*;
use multifilereader::*;
use prn_int::*;
use prn_char::*;
use prn_float::*;
use formatteriteminfo::*;

//This is available in some versions of std, but not all that we target.
macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

static VERSION: &'static str = env!("CARGO_PKG_VERSION");
const MAX_BYTES_PER_UNIT: usize = 8;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Radix { Decimal, Hexadecimal, Octal, NoPrefix }

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optopt("A", "address-radix",
                "Select the base in which file offsets are printed.", "RADIX");
    opts.optopt("j", "skip-bytes",
                "Skip bytes input bytes before formatting and writing.", "BYTES");
    opts.optopt("N", "read-bytes",
                "limit dump to BYTES input bytes", "BYTES");
    opts.optopt("", "endian", "byte order to use for multi-byte formats", "big|little");
    opts.optopt("S", "strings",
                ("output strings of at least BYTES graphic chars. 3 is assumed when \
                 BYTES is not specified."),
                "BYTES");
    opts.optflagmulti("a", "", "named characters, ignoring high-order bit");
    opts.optflagmulti("b", "", "octal bytes");
    opts.optflagmulti("c", "", "ASCII characters or backslash escapes");
    opts.optflagmulti("d", "", "unsigned decimal 2-byte units");
    opts.optflagmulti("D", "", "unsigned decimal 4-byte units");
    opts.optflagmulti("o", "", "unsigned decimal 2-byte units");

    opts.optflagmulti("I", "", "decimal 2-byte units");
    opts.optflagmulti("L", "", "decimal 2-byte units");
    opts.optflagmulti("i", "", "decimal 2-byte units");
    opts.optflagmulti("x", "", "hexadecimal 2-byte units");
    opts.optflagmulti("h", "", "hexadecimal 2-byte units");

    opts.optflagmulti("O", "", "octal 4-byte units");
    opts.optflagmulti("s", "", "decimal 4-byte units");
    opts.optflagmulti("X", "", "hexadecimal 4-byte units");
    opts.optflagmulti("H", "", "hexadecimal 4-byte units");

    opts.optflagmulti("e", "", "floating point double precision (64-bit) units");
    opts.optflagmulti("f", "", "floating point single precision (32-bit) units");
    opts.optflagmulti("F", "", "floating point double precision (64-bit) units");

    opts.optopt("t", "format", "select output format or formats", "TYPE");
    opts.optflag("v", "output-duplicates", "do not use * to mark line suppression");
    opts.optflagopt("w", "width",
                ("output BYTES bytes per output line. 32 is implied when BYTES is not \
                 specified."),
                "BYTES");
    opts.optflag("h", "help", "display this help and exit.");
    opts.optflag("", "version", "output version information and exit.");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            disp_err!("{}", f);
            return 1;
        }
    };

    if matches.opt_present("h") {
        let msg = unindent(&format!("
                Usage:
                    {0} [OPTION]... [FILENAME]...

                Displays data in various human-readable formats.", executable!()));
        println!("{}", opts.usage(&msg));
        return 0;
    }
    if matches.opt_present("version") {
        println!("{} {}", executable!(), VERSION);
        return 0;
    }

    let input_offset_base = match parse_radix(matches.opt_str("A")) {
        Ok(r) => r,
        Err(f) => {
            disp_err!("Invalid -A/--address-radix\n{}", f);
            return 1;
        }
    };

    let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) {
        None => { ByteOrder::Native },
        Some("little") => { ByteOrder::Little },
        Some("big") => { ByteOrder::Big },
        Some(s) => {
            disp_err!("Invalid argument --endian={}", s);
            return 1;
        }
    };

    // Gather up file names - args which don't start with '-'
    let stdnionly = [InputSource::Stdin];
    let inputs = args[1..]
        .iter()
        .filter_map(|w| match w as &str {
            "--" => Some(InputSource::Stdin),
            o if o.starts_with("-") => None,
            x => Some(InputSource::FileName(x)),
        })
        .collect::<Vec<_>>();
    // If no input files named, use stdin.
    let inputs = if inputs.len() == 0 {
        &stdnionly[..]
    } else {
        &inputs[..]
    };
    // Gather up format flags, we don't use getopts becase we need keep them in order.
    let flags = args[1..]
        .iter()
        .filter_map(|w| match w as &str {
            "--" => None,
            o if o.starts_with("-") => Some(&o[1..]),
            _ => None,
        })
        .collect::<Vec<_>>();

// TODO: -t fmts
        let known_formats = hashmap![
            "a" => FORMAT_ITEM_A,
            "B" => FORMAT_ITEM_OCT16,
            "b" => FORMAT_ITEM_OCT8,
            "c" => FORMAT_ITEM_C,
            "D" => FORMAT_ITEM_DEC32U,
            "d" => FORMAT_ITEM_DEC16U,
            "e" => FORMAT_ITEM_F64,
            "F" => FORMAT_ITEM_F64,
            "f" => FORMAT_ITEM_F32,
            "H" => FORMAT_ITEM_HEX32,
            "X" => FORMAT_ITEM_HEX32,
            "o" => FORMAT_ITEM_OCT16,
            "x" => FORMAT_ITEM_HEX16,
            "h" => FORMAT_ITEM_HEX16,

            "I" => FORMAT_ITEM_DEC16S,
            "L" => FORMAT_ITEM_DEC16S,
            "i" => FORMAT_ITEM_DEC16S,

            "O" => FORMAT_ITEM_OCT32,
            "s" => FORMAT_ITEM_DEC16U
        ];

        let mut formats = Vec::new();

        for flag in flags.iter() {
            match known_formats.get(flag) {
                None => {} // not every option is a format
                Some(r) => {
                    formats.push(*r)
                }
            }
        }

        if formats.is_empty() {
            formats.push(FORMAT_ITEM_OCT16); // 2 byte octal is the default
        }

        let mut line_bytes = match matches.opt_default("w", "32") {
            None => 16,
            Some(s) => {
                match s.parse::<usize>() {
                    Ok(i) => { i }
                    Err(_) => { 2 }
                }
            }
        };
        let min_bytes = formats.iter().fold(1, |max, next| cmp::max(max, next.byte_size));
        if line_bytes % min_bytes != 0 {
            show_warning!("invalid width {}; using {} instead", line_bytes, min_bytes);
            line_bytes = min_bytes;
        }

        let output_duplicates = matches.opt_present("v");

        odfunc(line_bytes, input_offset_base, byte_order, &inputs, &formats[..], output_duplicates)
}

fn odfunc(line_bytes: usize, input_offset_base: Radix, byte_order: ByteOrder,
        fnames: &[InputSource], formats: &[FormatterItemInfo], output_duplicates: bool) -> i32 {

    let mut mf = MultifileReader::new(fnames);
    let mut addr = 0;
    let mut bytes: Vec<u8> = vec![b'\x00'; line_bytes];
    let mut previous_bytes = Vec::<u8>::with_capacity(line_bytes);
    let mut duplicate_line = false;

    let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.byte_size));
    let print_width_block = formats
        .iter()
        .fold(1, |max, next| {
            cmp::max(max, next.print_width * (byte_size_block / next.byte_size))
        });

    if byte_size_block > MAX_BYTES_PER_UNIT {
        panic!("{}-bits types are unsupported. Current max={}-bits.",
                8 * byte_size_block,
                8 * MAX_BYTES_PER_UNIT);
    }

    let mut spaced_formatters: Vec<SpacedFormatterItemInfo> = formats
        .iter()
        .map(|f| SpacedFormatterItemInfo { frm: *f, spacing: [0; MAX_BYTES_PER_UNIT] })
        .collect();

    // calculate proper alignment for each item
    for sf in &mut spaced_formatters {
        let mut byte_size = sf.frm.byte_size;
        let mut items_in_block = byte_size_block / byte_size;
        let thisblock_width = sf.frm.print_width * items_in_block;
        let mut missing_spacing = print_width_block - thisblock_width;

        while items_in_block > 0 {
            let avg_spacing: usize = missing_spacing / items_in_block;
            for i in 0..items_in_block {
                sf.spacing[i * byte_size] += avg_spacing;
                missing_spacing -= avg_spacing;
            }
            // this assumes the size of all types is a power of 2 (1, 2, 4, 8, 16, ...)
            items_in_block /= 2;
            byte_size *= 2;
        }
    }

    loop {
        // print each line data (or multi-format raster of several lines describing the same data).
        // TODO: we need to read more data in case a multi-byte sequence starts at the end of the line

        match mf.f_read(bytes.as_mut_slice()) {
            Ok(0) => {
                if input_offset_base != Radix::NoPrefix {
                    print!("{}\n", print_with_radix(input_offset_base, addr)); // print final offset
                }
                break;
            }
            Ok(n) => {
                // not enough byte for a whole element, this should only happen on the last line.
                if n != line_bytes {
                    // set zero bytes in the part of the buffer that will be used, but is not filled.
                    for i in n..line_bytes {
                        bytes[i] = 0;
                    }
                }

                if !output_duplicates && previous_bytes == bytes && n == line_bytes {
                    if !duplicate_line {
                        duplicate_line = true;
                        println!("*");
                    }
                }
                else {
                    duplicate_line = false;
                    previous_bytes.clone_from(&bytes);

                    print_bytes(byte_order, &bytes, n, &print_with_radix(input_offset_base, addr),
                        &spaced_formatters, byte_size_block);
                }

                addr += n;
            }
            Err(_) => {
                break;
            }
        };
    }

    if mf.any_err {
        1
    } else {
        0
    }
}

fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, prefix: &str,
        formats: &[SpacedFormatterItemInfo], byte_size_block: usize) {
    let mut first = true; // First line of a multi-format raster.
    for f in formats {
        let mut output_text = String::new();

        let mut b = 0;
        while b < length {
            let nextb = b + f.frm.byte_size;

            output_text.push_str(&format!("{:>width$}",
                    "",
                    width = f.spacing[b % byte_size_block]));

            match f.frm.formatter {
                FormatWriter::IntWriter(func) => {
                    let p: u64 = match f.frm.byte_size {
                        1 => {
                            bytes[b] as u64
                        }
                        2 => {
                            byte_order.read_u16(&bytes[b..nextb]) as u64
                        }
                        4 => {
                            byte_order.read_u32(&bytes[b..nextb]) as u64
                        }
                        8 => {
                            byte_order.read_u64(&bytes[b..nextb])
                        }
                        _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); }
                    };
                    output_text.push_str(&func(p, f.frm.byte_size, f.frm.print_width));
                }
                FormatWriter::FloatWriter(func) => {
                    let p: f64 = match f.frm.byte_size {
                        4 => {
                            byte_order.read_f32(&bytes[b..nextb]) as f64
                        }
                        8 => {
                            byte_order.read_f64(&bytes[b..nextb])
                        }
                        _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); }
                    };
                    output_text.push_str(&func(p));
                }
                FormatWriter::MultibyteWriter(func) => {
                    output_text.push_str(&func(&bytes[b..length]));
                }
            }
            b = nextb;
        }

        if first {
            print!("{}", prefix); // print offset
            // if printing in multiple formats offset is printed only once
            first = false;
        }
        else {
            // this takes the space of the file offset on subsequent
            // lines of multi-format rasters.
            print!("{:>width$}", "", width=prefix.chars().count());
        }
        print!("{}\n", output_text);
    }
}

// For file byte offset printed at left margin.
fn parse_radix(radix_str: Option<String>) -> Result<Radix, &'static str> {
    match radix_str {
        None => Ok(Radix::Octal),
        Some(s) => {
            let st = s.into_bytes();
            if st.len() != 1 {
                Err("Radix must be one of [d, o, n, x]\n")
            } else {
                let radix: char = *(st.get(0)
                                      .expect("byte string of length 1 lacks a 0th elem")) as char;
                match radix {
                    'd' => Ok(Radix::Decimal),
                    'x' => Ok(Radix::Hexadecimal),
                    'o' => Ok(Radix::Octal),
                    'n' => Ok(Radix::NoPrefix),
                    _ => Err("Radix must be one of [d, o, n, x]\n")
                }
            }
        }
    }
}

fn print_with_radix(r: Radix, x: usize) -> String{
    match r {
        Radix::Decimal => format!("{:07}", x),
        Radix::Hexadecimal => format!("{:06X}", x),
        Radix::Octal => format!("{:07o}", x),
        Radix::NoPrefix => String::from(""),
    }
}

struct SpacedFormatterItemInfo {
    frm: FormatterItemInfo,
    spacing: [usize; MAX_BYTES_PER_UNIT],
}
<commit_msg>od: fix performance issue with large values of -w<commit_after>#![crate_name = "uu_od"]

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

extern crate getopts;
extern crate unindent;
extern crate byteorder;

#[macro_use]
extern crate uucore;

mod multifilereader;
mod byteorder_io;
mod formatteriteminfo;
mod prn_int;
mod prn_char;
mod prn_float;

use std::cmp;
use std::io::Write;
use unindent::*;
use byteorder_io::*;
use multifilereader::*;
use prn_int::*;
use prn_char::*;
use prn_float::*;
use formatteriteminfo::*;

//This is available in some versions of std, but not all that we target.
macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

static VERSION: &'static str = env!("CARGO_PKG_VERSION");
const MAX_BYTES_PER_UNIT: usize = 8;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Radix { Decimal, Hexadecimal, Octal, NoPrefix }

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optopt("A", "address-radix",
                "Select the base in which file offsets are printed.", "RADIX");
    opts.optopt("j", "skip-bytes",
                "Skip bytes input bytes before formatting and writing.", "BYTES");
    opts.optopt("N", "read-bytes",
                "limit dump to BYTES input bytes", "BYTES");
    opts.optopt("", "endian", "byte order to use for multi-byte formats", "big|little");
    opts.optopt("S", "strings",
                ("output strings of at least BYTES graphic chars. 3 is assumed when \
                 BYTES is not specified."),
                "BYTES");
    opts.optflagmulti("a", "", "named characters, ignoring high-order bit");
    opts.optflagmulti("b", "", "octal bytes");
    opts.optflagmulti("c", "", "ASCII characters or backslash escapes");
    opts.optflagmulti("d", "", "unsigned decimal 2-byte units");
    opts.optflagmulti("D", "", "unsigned decimal 4-byte units");
    opts.optflagmulti("o", "", "unsigned decimal 2-byte units");

    opts.optflagmulti("I", "", "decimal 2-byte units");
    opts.optflagmulti("L", "", "decimal 2-byte units");
    opts.optflagmulti("i", "", "decimal 2-byte units");
    opts.optflagmulti("x", "", "hexadecimal 2-byte units");
    opts.optflagmulti("h", "", "hexadecimal 2-byte units");

    opts.optflagmulti("O", "", "octal 4-byte units");
    opts.optflagmulti("s", "", "decimal 4-byte units");
    opts.optflagmulti("X", "", "hexadecimal 4-byte units");
    opts.optflagmulti("H", "", "hexadecimal 4-byte units");

    opts.optflagmulti("e", "", "floating point double precision (64-bit) units");
    opts.optflagmulti("f", "", "floating point single precision (32-bit) units");
    opts.optflagmulti("F", "", "floating point double precision (64-bit) units");

    opts.optopt("t", "format", "select output format or formats", "TYPE");
    opts.optflag("v", "output-duplicates", "do not use * to mark line suppression");
    opts.optflagopt("w", "width",
                ("output BYTES bytes per output line. 32 is implied when BYTES is not \
                 specified."),
                "BYTES");
    opts.optflag("h", "help", "display this help and exit.");
    opts.optflag("", "version", "output version information and exit.");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            disp_err!("{}", f);
            return 1;
        }
    };

    if matches.opt_present("h") {
        let msg = unindent(&format!("
                Usage:
                    {0} [OPTION]... [FILENAME]...

                Displays data in various human-readable formats.", executable!()));
        println!("{}", opts.usage(&msg));
        return 0;
    }
    if matches.opt_present("version") {
        println!("{} {}", executable!(), VERSION);
        return 0;
    }

    let input_offset_base = match parse_radix(matches.opt_str("A")) {
        Ok(r) => r,
        Err(f) => {
            disp_err!("Invalid -A/--address-radix\n{}", f);
            return 1;
        }
    };

    let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) {
        None => { ByteOrder::Native },
        Some("little") => { ByteOrder::Little },
        Some("big") => { ByteOrder::Big },
        Some(s) => {
            disp_err!("Invalid argument --endian={}", s);
            return 1;
        }
    };

    // Gather up file names - args which don't start with '-'
    let stdnionly = [InputSource::Stdin];
    let inputs = args[1..]
        .iter()
        .filter_map(|w| match w as &str {
            "--" => Some(InputSource::Stdin),
            o if o.starts_with("-") => None,
            x => Some(InputSource::FileName(x)),
        })
        .collect::<Vec<_>>();
    // If no input files named, use stdin.
    let inputs = if inputs.len() == 0 {
        &stdnionly[..]
    } else {
        &inputs[..]
    };
    // Gather up format flags, we don't use getopts becase we need keep them in order.
    let flags = args[1..]
        .iter()
        .filter_map(|w| match w as &str {
            "--" => None,
            o if o.starts_with("-") => Some(&o[1..]),
            _ => None,
        })
        .collect::<Vec<_>>();

// TODO: -t fmts
        let known_formats = hashmap![
            "a" => FORMAT_ITEM_A,
            "B" => FORMAT_ITEM_OCT16,
            "b" => FORMAT_ITEM_OCT8,
            "c" => FORMAT_ITEM_C,
            "D" => FORMAT_ITEM_DEC32U,
            "d" => FORMAT_ITEM_DEC16U,
            "e" => FORMAT_ITEM_F64,
            "F" => FORMAT_ITEM_F64,
            "f" => FORMAT_ITEM_F32,
            "H" => FORMAT_ITEM_HEX32,
            "X" => FORMAT_ITEM_HEX32,
            "o" => FORMAT_ITEM_OCT16,
            "x" => FORMAT_ITEM_HEX16,
            "h" => FORMAT_ITEM_HEX16,

            "I" => FORMAT_ITEM_DEC16S,
            "L" => FORMAT_ITEM_DEC16S,
            "i" => FORMAT_ITEM_DEC16S,

            "O" => FORMAT_ITEM_OCT32,
            "s" => FORMAT_ITEM_DEC16U
        ];

        let mut formats = Vec::new();

        for flag in flags.iter() {
            match known_formats.get(flag) {
                None => {} // not every option is a format
                Some(r) => {
                    formats.push(*r)
                }
            }
        }

        if formats.is_empty() {
            formats.push(FORMAT_ITEM_OCT16); // 2 byte octal is the default
        }

        let mut line_bytes = match matches.opt_default("w", "32") {
            None => 16,
            Some(s) => {
                match s.parse::<usize>() {
                    Ok(i) => { i }
                    Err(_) => { 2 }
                }
            }
        };
        let min_bytes = formats.iter().fold(1, |max, next| cmp::max(max, next.byte_size));
        if line_bytes % min_bytes != 0 {
            show_warning!("invalid width {}; using {} instead", line_bytes, min_bytes);
            line_bytes = min_bytes;
        }

        let output_duplicates = matches.opt_present("v");

        odfunc(line_bytes, input_offset_base, byte_order, &inputs, &formats[..], output_duplicates)
}

fn odfunc(line_bytes: usize, input_offset_base: Radix, byte_order: ByteOrder,
        fnames: &[InputSource], formats: &[FormatterItemInfo], output_duplicates: bool) -> i32 {

    let mut mf = MultifileReader::new(fnames);
    let mut addr = 0;
    let mut duplicate_line = false;
    let mut previous_bytes: Vec<u8> = Vec::new();
    let mut bytes: Vec<u8> = Vec::with_capacity(line_bytes);
    unsafe { bytes.set_len(line_bytes); } // fast but uninitialized

    let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.byte_size));
    let print_width_block = formats
        .iter()
        .fold(1, |max, next| {
            cmp::max(max, next.print_width * (byte_size_block / next.byte_size))
        });

    if byte_size_block > MAX_BYTES_PER_UNIT {
        panic!("{}-bits types are unsupported. Current max={}-bits.",
                8 * byte_size_block,
                8 * MAX_BYTES_PER_UNIT);
    }

    let mut spaced_formatters: Vec<SpacedFormatterItemInfo> = formats
        .iter()
        .map(|f| SpacedFormatterItemInfo { frm: *f, spacing: [0; MAX_BYTES_PER_UNIT] })
        .collect();

    // calculate proper alignment for each item
    for sf in &mut spaced_formatters {
        let mut byte_size = sf.frm.byte_size;
        let mut items_in_block = byte_size_block / byte_size;
        let thisblock_width = sf.frm.print_width * items_in_block;
        let mut missing_spacing = print_width_block - thisblock_width;

        while items_in_block > 0 {
            let avg_spacing: usize = missing_spacing / items_in_block;
            for i in 0..items_in_block {
                sf.spacing[i * byte_size] += avg_spacing;
                missing_spacing -= avg_spacing;
            }
            // this assumes the size of all types is a power of 2 (1, 2, 4, 8, 16, ...)
            items_in_block /= 2;
            byte_size *= 2;
        }
    }

    loop {
        // print each line data (or multi-format raster of several lines describing the same data).
        // TODO: we need to read more data in case a multi-byte sequence starts at the end of the line

        match mf.f_read(bytes.as_mut_slice()) {
            Ok(0) => {
                if input_offset_base != Radix::NoPrefix {
                    print!("{}\n", print_with_radix(input_offset_base, addr)); // print final offset
                }
                break;
            }
            Ok(n) => {
                // not enough byte for a whole element, this should only happen on the last line.
                if n != line_bytes {
                    // set zero bytes in the part of the buffer that will be used, but is not filled.
                    let mut max_used = n + MAX_BYTES_PER_UNIT;
                    if max_used > line_bytes {
                        max_used = line_bytes;
                    }

                    for i in n..max_used {
                        bytes[i] = 0;
                    }
                }

                if !output_duplicates && n == line_bytes && previous_bytes == bytes {
                    if !duplicate_line {
                        duplicate_line = true;
                        println!("*");
                    }
                }
                else {
                    duplicate_line = false;
                    if n == line_bytes {
                        // save a copy of the input unless it is the last line
                        previous_bytes.clone_from(&bytes);
                    }

                    print_bytes(byte_order, &bytes, n, &print_with_radix(input_offset_base, addr),
                        &spaced_formatters, byte_size_block);
                }

                addr += n;
            }
            Err(_) => {
                break;
            }
        };
    }

    if mf.any_err {
        1
    } else {
        0
    }
}

fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, prefix: &str,
        formats: &[SpacedFormatterItemInfo], byte_size_block: usize) {
    let mut first = true; // First line of a multi-format raster.
    for f in formats {
        let mut output_text = String::new();

        let mut b = 0;
        while b < length {
            let nextb = b + f.frm.byte_size;

            output_text.push_str(&format!("{:>width$}",
                    "",
                    width = f.spacing[b % byte_size_block]));

            match f.frm.formatter {
                FormatWriter::IntWriter(func) => {
                    let p: u64 = match f.frm.byte_size {
                        1 => {
                            bytes[b] as u64
                        }
                        2 => {
                            byte_order.read_u16(&bytes[b..nextb]) as u64
                        }
                        4 => {
                            byte_order.read_u32(&bytes[b..nextb]) as u64
                        }
                        8 => {
                            byte_order.read_u64(&bytes[b..nextb])
                        }
                        _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); }
                    };
                    output_text.push_str(&func(p, f.frm.byte_size, f.frm.print_width));
                }
                FormatWriter::FloatWriter(func) => {
                    let p: f64 = match f.frm.byte_size {
                        4 => {
                            byte_order.read_f32(&bytes[b..nextb]) as f64
                        }
                        8 => {
                            byte_order.read_f64(&bytes[b..nextb])
                        }
                        _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); }
                    };
                    output_text.push_str(&func(p));
                }
                FormatWriter::MultibyteWriter(func) => {
                    output_text.push_str(&func(&bytes[b..length]));
                }
            }
            b = nextb;
        }

        if first {
            print!("{}", prefix); // print offset
            // if printing in multiple formats offset is printed only once
            first = false;
        }
        else {
            // this takes the space of the file offset on subsequent
            // lines of multi-format rasters.
            print!("{:>width$}", "", width=prefix.chars().count());
        }
        print!("{}\n", output_text);
    }
}

// For file byte offset printed at left margin.
fn parse_radix(radix_str: Option<String>) -> Result<Radix, &'static str> {
    match radix_str {
        None => Ok(Radix::Octal),
        Some(s) => {
            let st = s.into_bytes();
            if st.len() != 1 {
                Err("Radix must be one of [d, o, n, x]\n")
            } else {
                let radix: char = *(st.get(0)
                                      .expect("byte string of length 1 lacks a 0th elem")) as char;
                match radix {
                    'd' => Ok(Radix::Decimal),
                    'x' => Ok(Radix::Hexadecimal),
                    'o' => Ok(Radix::Octal),
                    'n' => Ok(Radix::NoPrefix),
                    _ => Err("Radix must be one of [d, o, n, x]\n")
                }
            }
        }
    }
}

fn print_with_radix(r: Radix, x: usize) -> String{
    match r {
        Radix::Decimal => format!("{:07}", x),
        Radix::Hexadecimal => format!("{:06X}", x),
        Radix::Octal => format!("{:07o}", x),
        Radix::NoPrefix => String::from(""),
    }
}

struct SpacedFormatterItemInfo {
    frm: FormatterItemInfo,
    spacing: [usize; MAX_BYTES_PER_UNIT],
}
<|endoftext|>


<commit_before>use std;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io;

use tempdir::TempDir;

use pipeline::{CompileOptions, OutputType};
use ir;
use codegen;

fn get_writer(output_path: &Option<PathBuf>) -> io::Result<Box<std::io::Write>> {
    if let Some(ref output_path) = *output_path {
        Ok(Box::new(std::fs::File::create(output_path)?))
    } else {
        Ok(Box::new(std::io::stdout()))
    }
}

pub fn main_outer(tu: ir::TranslationUnit,
                  input_path: &str,
                  options: &CompileOptions)
                  -> io::Result<()> {
    match options.output_type {
        OutputType::Check => Ok(()),
        OutputType::LLVM => {
            let mut writer = get_writer(&options.output_path)?;
            codegen::llvm_gen::gen_translation_unit(&mut writer, tu)
        }
        OutputType::Run => output_llvm(tu, input_path, options),
        OutputType::Exec => Ok(()),
        _ => Ok(()),
    }
}

fn executable_name(path: &str) -> String {
    let file_name = Path::new(path).file_name().unwrap().to_str().unwrap();

    let mut name_parts: Vec<_> = file_name.split('.').collect();
    let parts_len = name_parts.len();
    if parts_len > 1 {
        name_parts.pop();
    }

    name_parts.join(".")
}

fn output_llvm(tu: ir::TranslationUnit,
               input_path: &str,
               options: &CompileOptions)
               -> io::Result<()> {
    fn path_to_str(path: &PathBuf) -> &str {
        path.to_str().unwrap()
    }

    let exec_name = executable_name(input_path);

    let tmp_dir = TempDir::new("elang-compiler")?;
    let llvm_path = tmp_dir.path().join(format!("{}.ll", exec_name));
    let obj_path = tmp_dir.path().join(format!("{}.o", exec_name));
    let exec_path = tmp_dir.path().join(exec_name);

    let mut llvm_file = File::create(&llvm_path)?;
    codegen::llvm_gen::gen_translation_unit(&mut llvm_file, tu).expect("error llvm gen");

    if options.opt &&
       !run_command("opt",
                    &["-O3", "-S", path_to_str(&llvm_path), "-o", path_to_str(&llvm_path)])
                ?
                .success() {
        panic!("opt fail");
    }

    if !run_command("llc",
                    &["-filetype=obj", path_to_str(&llvm_path), "-o", path_to_str(&obj_path)])
                ?
                .success() {
        panic!("llc fail");
    }

    if !run_command("clang",
                    &[path_to_str(&obj_path), "-o", path_to_str(&exec_path)])
                ?
                .success() {
        panic!("clang fail");
    }

    if !run_command(exec_path, &[])?.success() {
        panic!("exec fail");
    }

    Ok(())
}

fn run_command<S: AsRef<std::ffi::OsStr>>(ex: S,
                                          args: &[&str])
                                          -> io::Result<std::process::ExitStatus> {
    std::process::Command::new(ex).args(args).status()
}
<commit_msg>Way better outer<commit_after>use std;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io;

use tempdir::TempDir;

use pipeline::{CompileOptions, OutputType};
use ir;
use codegen;

fn get_writer(output_path: &Option<PathBuf>) -> io::Result<Box<std::io::Write>> {
    if let Some(ref output_path) = *output_path {
        Ok(Box::new(std::fs::File::create(output_path)?))
    } else {
        Ok(Box::new(std::io::stdout()))
    }
}

pub fn main_outer(tu: ir::TranslationUnit,
                  input_path: &str,
                  options: &CompileOptions)
                  -> io::Result<()> {
    match options.output_type {
        OutputType::Check => Ok(()),
        OutputType::LLVM => {
            let mut writer = get_writer(&options.output_path)?;
            codegen::llvm_gen::gen_translation_unit(&mut writer, tu)
        }
        OutputType::Run => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_llvm(tu, input_path, tmp_dir.path(), options)?;
            if !run_command(exec_path, &[])?.success() {
                panic!("exec fail");
            }
            Ok(())
        }
        OutputType::Exec => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_llvm(tu, input_path, tmp_dir.path(), options)?;
            let default_path = PathBuf::from("a.out");
            std::fs::copy(exec_path, options.output_path.as_ref().unwrap_or(&default_path))?;
            Ok(())
        }
    }
}

fn executable_name(path: &str) -> String {
    let file_name = Path::new(path).file_name().unwrap().to_str().unwrap();

    let mut name_parts: Vec<_> = file_name.split('.').collect();
    let parts_len = name_parts.len();
    if parts_len > 1 {
        name_parts.pop();
    }

    name_parts.join(".")
}

fn output_llvm(tu: ir::TranslationUnit,
               input_path: &str,
               tmp_dir_path: &Path,
               options: &CompileOptions)
               -> io::Result<PathBuf> {
    fn path_to_str(path: &PathBuf) -> &str {
        path.to_str().unwrap()
    }

    let exec_name = executable_name(input_path);

    let llvm_path = tmp_dir_path.join(format!("{}.ll", exec_name));
    let obj_path = tmp_dir_path.join(format!("{}.o", exec_name));
    let exec_path = tmp_dir_path.join(exec_name);

    let mut llvm_file = File::create(&llvm_path)?;
    codegen::llvm_gen::gen_translation_unit(&mut llvm_file, tu).expect("error llvm gen");

    if options.opt &&
       !run_command("opt",
                    &["-O3", "-S", path_to_str(&llvm_path), "-o", path_to_str(&llvm_path)])
                ?
                .success() {
        panic!("opt fail");
    }

    if !run_command("llc",
                    &["-filetype=obj", path_to_str(&llvm_path), "-o", path_to_str(&obj_path)])
                ?
                .success() {
        panic!("llc fail");
    }

    if !run_command("clang",
                    &[path_to_str(&obj_path), "-o", path_to_str(&exec_path)])
                ?
                .success() {
        panic!("clang fail");
    }

    Ok(exec_path)
}

fn run_command<S: AsRef<std::ffi::OsStr>>(ex: S,
                                          args: &[&str])
                                          -> io::Result<std::process::ExitStatus> {
    std::process::Command::new(ex).args(args).status()
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Panic handling and stack unwinding

use core::fmt::{Arguments, Write};
use super::io::term;
use super::arch::drivers::vga::Color;

#[lang = "panic_fmt"]
#[no_mangle] #[inline(never)] #[cold]
pub extern fn rust_begin_unwind( args: Arguments
                               , file: &'static str
                               , line: usize ) -> !
{
    write!(term::CONSOLE.lock()
                        .set_colors(Color::White, Color::Red)
                        .clear()
          , "KERNEL PANIC: {} \
            \nSomething has gone horribly wrong in {} at line {}."
          , args
          , file, line
          );
    loop { }
}

/// Required for Rust stack unwinding
#[lang = "eh_personality"]
#[no_mangle] #[inline(never)] #[cold]
pub extern fn eh_personality() {
    // TODO: add support for stack unwinding
}

#[lang = "stack_exhausted"]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn __morestack() -> ! {
    loop { }
}

#[allow(non_snake_case)]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn _Unwind_Resume() -> ! {
    loop { }
}

// #[lang = "begin_unwind"]
// #[no_mangle] #[inline(never)] #[cold]
// pub fn begin_unwind<M: Send>(msg: M
//                             , file_line: &(&'static str, usize))  -> !
// { loop { } }
<commit_msg>THIS IS FINE<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Panic handling and stack unwinding

use core::fmt::{Arguments, Write};
use super::io::term;
use super::arch::drivers::vga::Color;

#[lang = "panic_fmt"]
#[no_mangle] #[inline(never)] #[cold]
pub extern fn rust_begin_unwind( args: Arguments
                               , file: &'static str
                               , line: usize ) -> !
{
    write!(term::CONSOLE.lock()
                        .set_colors(Color::White, Color::Red)
                        .clear()
          , "THIS IS FINE. Kernel panic: {} \
            \nSomething has gone horribly wrong in {} at line {}."
          , args
          , file, line
          );
    loop { }
}

/// Required for Rust stack unwinding
#[lang = "eh_personality"]
#[no_mangle] #[inline(never)] #[cold]
pub extern fn eh_personality() {
    // TODO: add support for stack unwinding
}

#[lang = "stack_exhausted"]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn __morestack() -> ! {
    loop { }
}

#[allow(non_snake_case)]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn _Unwind_Resume() -> ! {
    loop { }
}

// #[lang = "begin_unwind"]
// #[no_mangle] #[inline(never)] #[cold]
// pub fn begin_unwind<M: Send>(msg: M
//                             , file_line: &(&'static str, usize))  -> !
// { loop { } }
<|endoftext|>


<commit_before>//! The Lea compiler command line frontend

#![feature(libc)]

extern crate lea_compiler as compiler;
extern crate lea_ast as ast;

extern crate term;
extern crate libc;

use compiler::*;

use ast::span::*;

use std::env;
use std::fmt;
use std::default::Default;
use std::io::{self, Read, Write, Stderr};
use std::fs::File;
use std::path::Path;

macro_rules! printerr {
    ($($arg:tt)*) => {
        print_err_fmt(format_args!($($arg)*))
    }
}

fn print_err_fmt(fmt: fmt::Arguments) -> io::Result<()> {
    writeln!(&mut io::stderr(), "{}", fmt)
}

fn print_usage() {
    print!("\
usage: leac [options] <file>

Parses and checks the given file. If file is \"-\", reads stdin. When the compiler is done, this will actually compile the source code.

Options:
 -h, --help     Display this help text
 -v, --version  Display the version of the Lea package
");
}

fn print_version() {
    println!("Lea {}", env!("CARGO_PKG_VERSION"));
}

/// Compiles a piece of code. Prints all errors / warnings to stderr, using color if stderr is a
/// terminal.
fn compile(code: &str, filename: &str) -> io::Result<()> {
    let mut term = term::stderr();
    let stderr = io::stderr();
    let mut dummy = DummyTerm(stderr);
    let mut fmt_target: &mut term::Terminal<Output=Stderr> = if unsafe {
        libc::isatty(libc::STDERR_FILENO) != 0
    } {
        // we can print to the term (or at least try to)
        match term {
            Some(ref mut term) => &mut **term,
            None => &mut dummy,
        }
    } else {
        // use dummy, since we don't want escape sequences
        &mut dummy
    };

    match compile_str(code, filename, &CompileConfig::default()) {
        Err(e) => match e {
            ErrParse(err) => {
                try!(err.format(code.as_ref(), filename, fmt_target));
            },
            ErrCheck(errs) => {
                let mut i = 1;
                for err in &errs {
                    try!(err.format(code.as_ref(), filename, fmt_target));
                    if i < errs.len() - 1 { try!(write!(fmt_target, "\n")); }
                    i += 1;
                }
            },
            ErrLint(warns) => {
                let mut first = true;
                for w in &warns {
                    if first { first = false; } else { try!(write!(fmt_target, "\n")); }
                    try!(w.format(code.as_ref(), filename, fmt_target));
                }
            },
            ErrEmit(errs) => {
                for err in &errs {
                    let mut msg = err.msg.to_string();
                    if let Some(ref d) = err.detail {
                        msg.push_str(format!(" ({})", d).as_ref());
                    }

                    try!(fmt_target.fg(term::color::RED));
                    try!(write!(fmt_target, "{}", msg));
                    try!(fmt_target.reset());
                }
            }
        },
        Ok(output) => {
            let warns = output.warns;
            if warns.len() > 0 {
                for w in warns {
                    try!(w.format(code, filename, fmt_target));
                }
            }
        }
    }

    Ok(())
}

fn compile_file(filename: &str) -> io::Result<()> {
    match File::open(&Path::new(filename.clone())) {
        Ok(mut file) => {
            let mut code = String::new();
            match file.read_to_string(&mut code) {
                Ok(_) => {
                    compile(code.as_ref(), filename.as_ref())
                },
                Err(err) => {
                    printerr!("error reading file \"{}\": {}", filename, err)
                }
            }
        },
        Err(err) => {
            printerr!("error opening file \"{}\": {}", filename, err)
        }
    }
}

pub fn main() {
    let mut iter = env::args();
    let mut inputarg: Option<String> = None;
    let mut lastarg: Option<String> = None;

    iter.next();    // Consume first argument (exec name)
    for arg in iter {
        if let Some(..) = inputarg {
            printerr!("invalid argument: {}", lastarg.unwrap()).unwrap();
            print_usage();
            return;
        }

        match arg.as_ref() {
            "-h" | "--help" => {
                print_usage();
                return;
            }
            "-v" | "--version" => {
                print_version();
                return;
            }
            _ => {
                // Unknown arg, must be input file
                inputarg = Some(arg.clone());
            }
        }

        lastarg = Some(arg);
    }

    if let None = inputarg {
        printerr!("error: no input file").unwrap();
        print_usage();
        return;
    }

    let f_string = inputarg.unwrap();
    let f: &str = f_string.as_ref();
    if f == "-" {
        let mut code = String::new();
        io::stdin().read_to_string(&mut code).unwrap();

        compile(code.as_ref(), "<stdin>").unwrap();
    } else {
        compile_file(f).unwrap();
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        // TODO
    }
}
<commit_msg>leac: always try to open a term<commit_after>//! The Lea compiler command line frontend

extern crate lea_compiler as compiler;
extern crate lea_ast as ast;

extern crate term;

use compiler::*;

use ast::span::*;

use std::env;
use std::fmt;
use std::default::Default;
use std::io::{self, Read, Write, Stderr};
use std::fs::File;
use std::path::Path;

macro_rules! printerr {
    ($($arg:tt)*) => {
        print_err_fmt(format_args!($($arg)*))
    }
}

fn print_err_fmt(fmt: fmt::Arguments) -> io::Result<()> {
    writeln!(&mut io::stderr(), "{}", fmt)
}

fn print_usage() {
    print!("\
usage: leac [options] <file>

Parses and checks the given file. If file is \"-\", reads stdin. When the compiler is done, this will actually compile the source code.

Options:
 -h, --help     Display this help text
 -v, --version  Display the version of the Lea package
");
}

fn print_version() {
    println!("Lea {}", env!("CARGO_PKG_VERSION"));
}

/// Compiles a piece of code. Prints all errors / warnings to stderr, using color if stderr is a
/// terminal.
fn compile(code: &str, filename: &str) -> io::Result<()> {
    let mut term = term::stderr();
    let stderr = io::stderr();
    let mut dummy = DummyTerm(stderr);
    let mut fmt_target: &mut term::Terminal<Output=Stderr> = match term {
        Some(ref mut term) => &mut **term,
        None => &mut dummy,
    };

    match compile_str(code, filename, &CompileConfig::default()) {
        Err(e) => match e {
            ErrParse(err) => {
                try!(err.format(code.as_ref(), filename, fmt_target));
            },
            ErrCheck(errs) => {
                let mut i = 1;
                for err in &errs {
                    try!(err.format(code.as_ref(), filename, fmt_target));
                    if i < errs.len() - 1 { try!(write!(fmt_target, "\n")); }
                    i += 1;
                }
            },
            ErrLint(warns) => {
                let mut first = true;
                for w in &warns {
                    if first { first = false; } else { try!(write!(fmt_target, "\n")); }
                    try!(w.format(code.as_ref(), filename, fmt_target));
                }
            },
            ErrEmit(errs) => {
                for err in &errs {
                    let mut msg = err.msg.to_string();
                    if let Some(ref d) = err.detail {
                        msg.push_str(format!(" ({})", d).as_ref());
                    }

                    try!(fmt_target.fg(term::color::RED));
                    try!(write!(fmt_target, "{}", msg));
                    try!(fmt_target.reset());
                }
            }
        },
        Ok(output) => {
            let warns = output.warns;
            if warns.len() > 0 {
                for w in warns {
                    try!(w.format(code, filename, fmt_target));
                }
            }
        }
    }

    Ok(())
}

fn compile_file(filename: &str) -> io::Result<()> {
    match File::open(&Path::new(filename.clone())) {
        Ok(mut file) => {
            let mut code = String::new();
            match file.read_to_string(&mut code) {
                Ok(_) => {
                    compile(code.as_ref(), filename.as_ref())
                },
                Err(err) => {
                    printerr!("error reading file \"{}\": {}", filename, err)
                }
            }
        },
        Err(err) => {
            printerr!("error opening file \"{}\": {}", filename, err)
        }
    }
}

pub fn main() {
    let mut iter = env::args();
    let mut inputarg: Option<String> = None;
    let mut lastarg: Option<String> = None;

    iter.next();    // Consume first argument (exec name)
    for arg in iter {
        if let Some(..) = inputarg {
            printerr!("invalid argument: {}", lastarg.unwrap()).unwrap();
            print_usage();
            return;
        }

        match arg.as_ref() {
            "-h" | "--help" => {
                print_usage();
                return;
            }
            "-v" | "--version" => {
                print_version();
                return;
            }
            _ => {
                // Unknown arg, must be input file
                inputarg = Some(arg.clone());
            }
        }

        lastarg = Some(arg);
    }

    if let None = inputarg {
        printerr!("error: no input file").unwrap();
        print_usage();
        return;
    }

    let f_string = inputarg.unwrap();
    let f: &str = f_string.as_ref();
    if f == "-" {
        let mut code = String::new();
        io::stdin().read_to_string(&mut code).unwrap();

        compile(code.as_ref(), "<stdin>").unwrap();
    } else {
        compile_file(f).unwrap();
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        // TODO
    }
}
<|endoftext|>


<commit_before>use error::Error;
use json;
use std::default::Default;
use std::path::{Path, PathBuf};

#[derive(Debug, Default)]
pub struct Manifest {
    pub edition: Edition,
    pub targets: Vec<Target>,
}

impl Manifest {
    pub fn from_str(src: &str) -> Result<Self, Error> {
        let mut j = json::parse(src).map_err(Error::InvalidManifestJson)?;

        let edition: Edition = match j["edition"].as_str() {
            Some("2015") => Edition::E2015,
            Some("2018") => Edition::E2018,
            Some(unknown) => panic!("Unrecognized value for edition \"{}\"", unknown),
            None => Edition::default(),
        };

        let targets: Vec<Target> = j["targets"].members_mut().map(Target::from_json).collect();

        Result::Ok(Manifest { edition, targets })
    }

    pub fn custom_builds(&self) -> Vec<&Target> {
        self.targets
            .iter()
            .filter(|t| t.is_custom_build())
            .collect()
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Edition {
    E2015,
    E2018,
}

impl Default for Edition {
    fn default() -> Self {
        Edition::E2015
    }
}

#[derive(Debug, PartialEq)]
pub struct Target {
    kind: Vec<String>,
    crate_types: Vec<String>,
    name: String,
    src_path: PathBuf,
    edition: Option<String>,
}

impl Target {
    const LIB_KINDS: [&'static str; 4] = ["lib", "rlib", "dylib", "staticlib"];

    pub fn src_path(&self) -> &PathBuf {
        &self.src_path
    }

    fn from_json(j: &mut json::JsonValue) -> Target {
        let kind: Vec<String> = {
            assert!(j["kind"].is_array());
            j["kind"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let crate_types: Vec<String> = {
            assert!(j["crate_types"].is_array());
            j["crate_types"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let name: String = j["name"].take_string().expect("name is missing");
        let src_path: PathBuf =
            Path::new(&j["src_path"].take_string().expect("src_path is missing")).to_path_buf();
        let edition: Option<String> = j["edition"].take_string();
        Target {
            kind,
            crate_types,
            name,
            src_path,
            edition,
        }
    }

    fn is_bin(&self) -> bool {
        self.kind.contains(&String::from("bin"))
    }

    fn is_custom_build(&self) -> bool {
        self.kind.contains(&String::from("custom-build"))
    }

    fn is_lib(&self) -> bool {
        self.kind
            .iter()
            .find(|k| Self::LIB_KINDS.contains(&&k[..]))
            .is_some()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    fn read_manifest(filename: &str) -> Manifest {
        let manifest_str: String =
            fs::read_to_string(filename).expect("manifest file cannot be read");
        Manifest::from_str(&manifest_str).expect("manifest cannot be read")
    }

    #[test]
    fn manifest_with_edition2018_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-edition-2018.json");
        assert_eq!(Edition::E2018, manifest.edition);
    }

    #[test]
    fn manifest_with_edition2015_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-edition-2015.json");
        assert_eq!(Edition::E2015, manifest.edition);
    }

    #[test]
    fn manifest_without_edition_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-no-edition.json");
        assert_eq!(Edition::E2015, manifest.edition);
    }

    #[test]
    fn manifest_for_simple_lib() {
        let manifest = read_manifest("test-resources/example-lib.json");
        assert_eq!(
            Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib"),
                src_path: Path::new("/home/muhuk/Documents/code/example-lib/src/lib.rs")
                    .to_path_buf(),
                edition: Some(String::from("2018"))
            },
            manifest.targets[0]
        );
        assert!(manifest.targets[0].is_lib());
        assert!(!manifest.targets[0].is_bin());
    }

    #[test]
    fn manifest_for_simple_bin() {
        let manifest = read_manifest("test-resources/example-bin.json");
        assert_eq!(
            Target {
                kind: vec!(String::from("bin")),
                crate_types: vec!(String::from("bin")),
                name: String::from("example-bin"),
                src_path: Path::new("/home/muhuk/Documents/code/example-bin/src/main.rs")
                    .to_path_buf(),
                edition: Some(String::from("2018"))
            },
            manifest.targets[0]
        );
        assert!(manifest.targets[0].is_bin());
        assert!(!manifest.targets[0].is_lib());
    }

    #[test]
    fn manifest_with_custom_build() {
        let manifest = read_manifest("test-resources/example-custom-build.json");
        assert_eq!(
            vec![
                Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-custom-build"),
                    src_path: Path::new(
                        "/home/muhuk/Documents/code/example-custom-build/src/lib.rs"
                    )
                    .to_path_buf(),
                    edition: Some(String::from("2018"))
                },
                Target {
                    kind: vec!(String::from("custom-build")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("build-script-build"),
                    src_path: Path::new("/home/muhuk/Documents/code/example-custom-build/build.rs")
                        .to_path_buf(),
                    edition: Some(String::from("2018"))
                }
            ],
            manifest.targets
        );
        assert!(manifest.targets[1].is_custom_build());
    }
}
<commit_msg>Clippy<commit_after>use error::Error;
use json;
use std::default::Default;
use std::path::{Path, PathBuf};

#[derive(Debug, Default)]
pub struct Manifest {
    pub edition: Edition,
    pub targets: Vec<Target>,
}

impl Manifest {
    pub fn from_str(src: &str) -> Result<Self, Error> {
        let mut j = json::parse(src).map_err(Error::InvalidManifestJson)?;

        let edition: Edition = match j["edition"].as_str() {
            Some("2015") => Edition::E2015,
            Some("2018") => Edition::E2018,
            Some(unknown) => panic!("Unrecognized value for edition \"{}\"", unknown),
            None => Edition::default(),
        };

        let targets: Vec<Target> = j["targets"].members_mut().map(Target::from_json).collect();

        Result::Ok(Manifest { edition, targets })
    }

    pub fn custom_builds(&self) -> Vec<&Target> {
        self.targets
            .iter()
            .filter(|t| t.is_custom_build())
            .collect()
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Edition {
    E2015,
    E2018,
}

impl Default for Edition {
    fn default() -> Self {
        Edition::E2015
    }
}

#[derive(Debug, PartialEq)]
pub struct Target {
    kind: Vec<String>,
    crate_types: Vec<String>,
    name: String,
    src_path: PathBuf,
    edition: Option<String>,
}

impl Target {
    const LIB_KINDS: [&'static str; 4] = ["lib", "rlib", "dylib", "staticlib"];

    pub fn src_path(&self) -> &PathBuf {
        &self.src_path
    }

    fn from_json(j: &mut json::JsonValue) -> Target {
        let kind: Vec<String> = {
            assert!(j["kind"].is_array());
            j["kind"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let crate_types: Vec<String> = {
            assert!(j["crate_types"].is_array());
            j["crate_types"]
                .members_mut()
                .map(|k| k.take_string().unwrap())
                .collect()
        };
        let name: String = j["name"].take_string().expect("name is missing");
        let src_path: PathBuf =
            Path::new(&j["src_path"].take_string().expect("src_path is missing")).to_path_buf();
        let edition: Option<String> = j["edition"].take_string();
        Target {
            kind,
            crate_types,
            name,
            src_path,
            edition,
        }
    }

    fn is_bin(&self) -> bool {
        self.kind.contains(&String::from("bin"))
    }

    fn is_custom_build(&self) -> bool {
        self.kind.contains(&String::from("custom-build"))
    }

    fn is_lib(&self) -> bool {
        self.kind.iter().any(|k| Self::LIB_KINDS.contains(&&k[..]))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    fn read_manifest(filename: &str) -> Manifest {
        let manifest_str: String =
            fs::read_to_string(filename).expect("manifest file cannot be read");
        Manifest::from_str(&manifest_str).expect("manifest cannot be read")
    }

    #[test]
    fn manifest_with_edition2018_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-edition-2018.json");
        assert_eq!(Edition::E2018, manifest.edition);
    }

    #[test]
    fn manifest_with_edition2015_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-edition-2015.json");
        assert_eq!(Edition::E2015, manifest.edition);
    }

    #[test]
    fn manifest_without_edition_can_be_parsed() {
        let manifest = read_manifest("test-resources/example-no-edition.json");
        assert_eq!(Edition::E2015, manifest.edition);
    }

    #[test]
    fn manifest_for_simple_lib() {
        let manifest = read_manifest("test-resources/example-lib.json");
        assert_eq!(
            Target {
                kind: vec!(String::from("lib")),
                crate_types: vec!(String::from("lib")),
                name: String::from("example-lib"),
                src_path: Path::new("/home/muhuk/Documents/code/example-lib/src/lib.rs")
                    .to_path_buf(),
                edition: Some(String::from("2018"))
            },
            manifest.targets[0]
        );
        assert!(manifest.targets[0].is_lib());
        assert!(!manifest.targets[0].is_bin());
    }

    #[test]
    fn manifest_for_simple_bin() {
        let manifest = read_manifest("test-resources/example-bin.json");
        assert_eq!(
            Target {
                kind: vec!(String::from("bin")),
                crate_types: vec!(String::from("bin")),
                name: String::from("example-bin"),
                src_path: Path::new("/home/muhuk/Documents/code/example-bin/src/main.rs")
                    .to_path_buf(),
                edition: Some(String::from("2018"))
            },
            manifest.targets[0]
        );
        assert!(manifest.targets[0].is_bin());
        assert!(!manifest.targets[0].is_lib());
    }

    #[test]
    fn manifest_with_custom_build() {
        let manifest = read_manifest("test-resources/example-custom-build.json");
        assert_eq!(
            vec![
                Target {
                    kind: vec!(String::from("lib")),
                    crate_types: vec!(String::from("lib")),
                    name: String::from("example-custom-build"),
                    src_path: Path::new(
                        "/home/muhuk/Documents/code/example-custom-build/src/lib.rs"
                    )
                    .to_path_buf(),
                    edition: Some(String::from("2018"))
                },
                Target {
                    kind: vec!(String::from("custom-build")),
                    crate_types: vec!(String::from("bin")),
                    name: String::from("build-script-build"),
                    src_path: Path::new("/home/muhuk/Documents/code/example-custom-build/build.rs")
                        .to_path_buf(),
                    edition: Some(String::from("2018"))
                }
            ],
            manifest.targets
        );
        assert!(manifest.targets[1].is_custom_build());
    }
}
<|endoftext|>