<commit_before>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32
}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
pub enum Destination {
    /// A register
    Register(Register),
}

impl Node {
    /// Create a `Node` with defaults for accumulator and backup registers
    pub fn new() -> Node {
        Node { acc: 0, bac: 0 }
    }

    /// Create a `Node` with prescribed values for accumulator and backup registers
    pub fn with(acc: i32, bac: i32) -> Node {
        Node { acc: acc, bac: bac }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        Node { acc: self.acc, bac: self.bac }
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.move_value(value, destination)
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => Node { acc: value, bac: self.bac },
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        Node { acc: self.bac, bac: self.acc }
    }

    fn save(&self) -> Node {
        Node { acc: self.acc, bac: self.acc }
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        Node { acc: self.acc + value, bac: self.bac }
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        Node { acc: self.acc - value, bac: self.bac }
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1,0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }
}

<commit_msg>Introduce a program counter<commit_after>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32,
    pc: isize,
}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
pub enum Destination {
    /// A register
    Register(Register),
}

impl Node {
    /// Create a `Node` with defaults for accumulator and backup registers
    pub fn new() -> Node {
        Node { acc: 0, bac: 0, pc: 0 }
    }

    /// Create a `Node` with prescribed values for accumulator and backup registers
    pub fn with(acc: i32, bac: i32, pc: isize) -> Node {
        Node { acc: acc, bac: bac, pc: pc }
    }

    /// Create a `Node` from self with the program counter incremented
    pub fn increment_pc(&self) -> Node {
        Node { pc: self.pc + 1, .. *self }
    }

    /// Create a `Node` from self with a prescribed accumulator register value
    pub fn set_acc(&self, acc: i32) -> Node {
        Node { acc: acc, .. *self }
    }

    /// Create a `Node` from self with a prescribed backup register value
    pub fn set_bac(&self, bac: i32) -> Node {
        Node { bac: bac, .. *self }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        self.increment_pc()
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.move_value(value, destination)
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => self.increment_pc().set_acc(value),
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        let acc: i32 = self.acc;
        let bac: i32 = self.bac;

        self.increment_pc().set_acc(bac).set_bac(acc)
    }

    fn save(&self) -> Node {
        self.increment_pc().set_bac(self.acc)
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc + value)
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        };

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc - value)
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1, 0);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::with(0, 0, 0);
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0, 1), next);
    }
}

<|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.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs;
use std::fs::Metadata;
use std::path::{Path, PathBuf};
use std::time::{SystemTime};

use log::warn;
use rrg_proto::{GetFileStatRequest, path_spec::Options, path_spec::PathType, PathSpec, StatEntry};

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Response {
    metadata: Metadata,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    symlink: Option<PathBuf>,
    path: PathBuf,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)?
    } else {
        std::fs::symlink_metadata(&request.path)?
    };

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let mut response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        ext_attrs: vec!(),
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    if request.collect_ext_attrs {
        // TODO: This is not pretty. Consider creating a blank `ext_attrs`
        // implementation for Windows and make this code compile regardless of
        // the platform.
        #[cfg(target_family = "unix")]
        {
            // TODO: Do not fail on error.
            response.ext_attrs.extend(crate::fs::unix::ext_attrs(&response.path)?);
        }
    }

    session.reply(response)?;
    Ok(())
}

#[cfg(target_os = "linux")]
fn get_time_option<E: std::fmt::Display>(time: Result<SystemTime, E>) -> Option<SystemTime> {
    match time {
        Ok(time_value) => Some(time_value),
        Err(err) => {
            warn!("Unable to get time value: {}", err);
            None
        }
    }
}

fn get_time_since_unix_epoch(sys_time: &Option<SystemTime>) -> Option<u64> {
    match sys_time {
        Some(time_value) => match rrg_proto::micros(*time_value) {
            Ok(micros_value) => Some(micros_value),
            Err(error) => {
                warn!("failed to convert time: {}", error);
                None
            }
        }
        None => None
    }
}

#[cfg(target_os = "linux")]
fn get_status_change_time(metadata: &fs::Metadata) -> Option<SystemTime> {
    use std::time::Duration;
    use std::os::unix::fs::MetadataExt;
    use std::time::UNIX_EPOCH;

    UNIX_EPOCH.checked_add(Duration::from_secs(metadata.ctime() as u64))
}

fn collapse_pathspec(pathspec: PathSpec) -> PathBuf {
    fn recursive_collapse(pathspec: PathSpec) -> PathBuf {
        match pathspec.path {
            Some(path) => {
                let path_buf = PathBuf::from(path);
                match pathspec.nested_path {
                    Some(nested_path_box) => path_buf.join(recursive_collapse(*nested_path_box)),
                    None => path_buf,
                }
            }
            None => PathBuf::default()
        }
    }

    let mut result = recursive_collapse(pathspec);
    if !result.has_root() {
        result = PathBuf::from("/").join(result);
    }
    result
}

impl super::Request for Request {

    type Proto = GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        match proto.pathspec {
            Some(pathspec) => Ok(Request {
                path: collapse_pathspec(pathspec),
                collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
                follow_symlink: proto.follow_symlink.unwrap_or(false),
            }),

            None => Err(session::ParseError::from(
                session::MissingFieldError::new("path spec"))),
        }
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            st_flags_linux: self.flags_linux,
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::tempdir;

    use crate::action::Request;

    use super::*;

    #[test]
    fn test_path_collapse() {
        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("file")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("to")),
                    ..Default::default()
                })),
            path: Some(String::from("path")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/file"));

        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("on/device")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("some/file")),
                    ..Default::default()
                })),
            path: Some(String::from("path/to")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/some/file/on/device"));
    }
}
<commit_msg>Simplify parsing of stat action requests.<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.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs;
use std::fs::Metadata;
use std::path::{Path, PathBuf};
use std::time::{SystemTime};

use log::warn;
use rrg_proto::{GetFileStatRequest, path_spec::Options, path_spec::PathType, PathSpec, StatEntry};

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Response {
    metadata: Metadata,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    symlink: Option<PathBuf>,
    path: PathBuf,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)?
    } else {
        std::fs::symlink_metadata(&request.path)?
    };

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let mut response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        ext_attrs: vec!(),
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    if request.collect_ext_attrs {
        // TODO: This is not pretty. Consider creating a blank `ext_attrs`
        // implementation for Windows and make this code compile regardless of
        // the platform.
        #[cfg(target_family = "unix")]
        {
            // TODO: Do not fail on error.
            response.ext_attrs.extend(crate::fs::unix::ext_attrs(&response.path)?);
        }
    }

    session.reply(response)?;
    Ok(())
}

#[cfg(target_os = "linux")]
fn get_time_option<E: std::fmt::Display>(time: Result<SystemTime, E>) -> Option<SystemTime> {
    match time {
        Ok(time_value) => Some(time_value),
        Err(err) => {
            warn!("Unable to get time value: {}", err);
            None
        }
    }
}

fn get_time_since_unix_epoch(sys_time: &Option<SystemTime>) -> Option<u64> {
    match sys_time {
        Some(time_value) => match rrg_proto::micros(*time_value) {
            Ok(micros_value) => Some(micros_value),
            Err(error) => {
                warn!("failed to convert time: {}", error);
                None
            }
        }
        None => None
    }
}

#[cfg(target_os = "linux")]
fn get_status_change_time(metadata: &fs::Metadata) -> Option<SystemTime> {
    use std::time::Duration;
    use std::os::unix::fs::MetadataExt;
    use std::time::UNIX_EPOCH;

    UNIX_EPOCH.checked_add(Duration::from_secs(metadata.ctime() as u64))
}

fn collapse_pathspec(pathspec: PathSpec) -> PathBuf {
    fn recursive_collapse(pathspec: PathSpec) -> PathBuf {
        match pathspec.path {
            Some(path) => {
                let path_buf = PathBuf::from(path);
                match pathspec.nested_path {
                    Some(nested_path_box) => path_buf.join(recursive_collapse(*nested_path_box)),
                    None => path_buf,
                }
            }
            None => PathBuf::default()
        }
    }

    let mut result = recursive_collapse(pathspec);
    if !result.has_root() {
        result = PathBuf::from("/").join(result);
    }
    result
}

impl super::Request for Request {

    type Proto = GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            st_flags_linux: self.flags_linux,
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::tempdir;

    use crate::action::Request;

    use super::*;

    #[test]
    fn test_path_collapse() {
        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("file")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("to")),
                    ..Default::default()
                })),
            path: Some(String::from("path")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/file"));

        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("on/device")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("some/file")),
                    ..Default::default()
                })),
            path: Some(String::from("path/to")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/some/file/on/device"));
    }
}
<|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.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Session};

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

#[derive(Debug)]
pub struct Response {
    path: PathBuf,
    metadata: Metadata,
    symlink: Option<PathBuf>,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
}

#[derive(Debug)]
enum Error {
    Metadata(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Metadata(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Metadata(ref error) => {
                write!(fmt, "unable to collect metadata: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)
    } else {
        std::fs::symlink_metadata(&request.path)
    }.map_err(Error::Metadata)?;

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_family = "unix")]
    let ext_attrs = if request.collect_ext_attrs {
        // TODO: Make the `ack!` macro more expressive and then simplify it.
        match crate::fs::unix::ext_attrs(&request.path) {
            Ok(ext_attrs) => ext_attrs.collect(),
            Err(error) => {
                warn! {
                    "failed to collect attributes for '{path}': {cause}",
                    path = request.path.display(),
                    cause = error,
                };
                vec!()
            },
        }
    } else {
        vec!()
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: ext_attrs,
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    session.reply(response)?;

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
}
<commit_msg>Write documentation for the stat action.<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.

//! A handler and associated types for the file stat action.
//!
//! A file stat action collects filesystem metadata associated with a particular
//! file.
//!
//! Note that the gathered bits of information differ across platforms, e.g. on
//! Linux there is a notion of symlinks whereas on Windows no such thing exists.
//! Therefore, on Linux the results might include additional information about
//! the symlink (like the file it points to).

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Session};

/// A request type for the stat action.
#[derive(Debug)]
pub struct Request {
    /// A path to the file to stat.
    path: PathBuf,
    /// Whether to collect extended file attributes.
    collect_ext_attrs: bool,
    /// Whether, in case of a symlink, to collect data about the linked file.
    follow_symlink: bool,
}

/// A response type for the stat action.
#[derive(Debug)]
pub struct Response {
    /// A path to the file that the result corresponds to.
    path: PathBuf,
    /// Metadata about the file.
    metadata: Metadata,
    /// A path to the pointed file (in case of a symlink).
    symlink: Option<PathBuf>,
    /// Extended attributes of the file.
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    /// Additional Linux-specific file flags.
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
}

/// An error type for failures that can occur during the stat action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to collect file metadata.
    Metadata(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Metadata(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Metadata(ref error) => {
                write!(fmt, "unable to collect metadata: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the file stat action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)
    } else {
        std::fs::symlink_metadata(&request.path)
    }.map_err(Error::Metadata)?;

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_family = "unix")]
    let ext_attrs = if request.collect_ext_attrs {
        // TODO: Make the `ack!` macro more expressive and then simplify it.
        match crate::fs::unix::ext_attrs(&request.path) {
            Ok(ext_attrs) => ext_attrs.collect(),
            Err(error) => {
                warn! {
                    "failed to collect attributes for '{path}': {cause}",
                    path = request.path.display(),
                    cause = error,
                };
                vec!()
            },
        }
    } else {
        vec!()
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: ext_attrs,
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    session.reply(response)?;

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

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


<commit_before>extern crate regex;
#[macro_use]
extern crate lazy_static;
extern crate num;
#[macro_use]
extern crate log;

mod lexer;
mod parser;
mod value;
mod interpreter;

use std::collections::HashMap;
use std::io;
use std::io::Write;
use value::Value;
use interpreter::RuntimeItem;

static IT_IDENT: &'static str = "it";

fn main() {
    let mut ctx = {
        let mut map: HashMap<String, RuntimeItem> = HashMap::new();
        map.insert(IT_IDENT.to_string(), RuntimeItem::Value(Value::Integer(0)));
        map.insert("pi".to_string(), RuntimeItem::Value(Value::Float(3.1415926535897932384626433832795)));
        map.insert("e".to_string(), RuntimeItem::Value(Value::Float(2.7182818284590452353602874713527)));
        interpreter::context_from_hashmap(map)
    };

    loop {
        print!("> ");
        io::stdout().flush().expect("stdout error");

        let line = {
            let mut buf = String::new();
            match io::stdin().read_line(&mut buf) {
                Ok(_) => buf,
                Err(e) => {
                    println!("{:?}", e);
                    "".to_string()
                },
            }
        };

        match line.trim() {
            "#q" => break,
            "" => (),
            line => {
                match process_line(line, &mut *ctx) {
                    Ok(item) => {
                        if let RuntimeItem::Value(v) = item {
                            print_value(&v);
                            ctx.put(IT_IDENT, item);
                        } else {
                            println!("Function OK");
                        }
                    },
                    Err(msg) => println!("{}", msg),
                }
            }
        }
    }
}

fn print_value(v: &Value) {
    match *v {
        Value::Float(f) => println!("= {}", f),
        Value::Integer(n) => {
            println!("= {}", n);
            println!("  {:#x}", n);
            println!("  {:#b}", n);
        },
    }
}

fn process_line(line: &str, ctx: &mut interpreter::Context) -> Result<RuntimeItem, String> {
    let input = try!(lexer::lex(&line));
    info!("Tokens: {:?}", input);

    let stmt = try!(parser::parse(&input));
    info!("Ast: {:?}", stmt);

    interpreter::interpret(&stmt, ctx)
}
<commit_msg>started with #13<commit_after>extern crate regex;
#[macro_use]
extern crate lazy_static;
extern crate num;
#[macro_use]
extern crate log;

mod lexer;
mod parser;
mod value;
mod interpreter;

use std::collections::HashMap;
use std::io;
use std::io::Write;
use value::Value;
use interpreter::{ RuntimeItem, Context };

static IT_IDENT: &'static str = "it";

struct Calculator {
    ctx: Box<Context>,
}

impl Calculator {
    pub fn new() -> Calculator {
        let mut ctx = {
            let mut map: HashMap<String, RuntimeItem> = HashMap::new();
            map.insert(IT_IDENT.to_string(), RuntimeItem::Value(Value::Integer(0)));
            map.insert("pi".to_string(), RuntimeItem::Value(Value::Float(3.1415926535897932384626433832795)));
            map.insert("e".to_string(), RuntimeItem::Value(Value::Float(2.7182818284590452353602874713527)));
            interpreter::context_from_hashmap(map)
        };
        Calculator { ctx: ctx }
    }

    fn calc(&mut self, src: &str) -> Result<RuntimeItem, String> {
        let input = try!(lexer::lex(&src));
        info!("Tokens: {:?}", input);

        let stmt = try!(parser::parse(&input));
        info!("Ast: {:?}", stmt);

        interpreter::interpret(&stmt, &mut *self.ctx)
    }
}

fn main() {
    let mut calculator = Calculator::new();

    loop {
        print!("> ");
        io::stdout().flush().expect("stdout error");

        let line = {
            let mut buf = String::new();
            match io::stdin().read_line(&mut buf) {
                Ok(_) => buf,
                Err(e) => {
                    println!("{:?}", e);
                    "".to_string()
                },
            }
        };

        match line.trim() {
            "#q" => break,
            "" => (),
            line => {
                match calculator.calc(line) {
                    Ok(item) => {
                        if let RuntimeItem::Value(v) = item {
                            print_value(&v);
                            ctx.put(IT_IDENT, item);
                        } else {
                            println!("Function OK");
                        }
                    },
                    Err(msg) => println!("{}", msg),
                }
            }
        }
    }
}

fn print_value(v: &Value) {
    match *v {
        Value::Float(f) => println!("= {}", f),
        Value::Integer(n) => {
            println!("= {}", n);
            println!("  {:#x}", n);
            println!("  {:#b}", n);
        },
    }
}
<|endoftext|>


<commit_before>#[link(name="du", vers="1.0.0", author="Derek Chiang")];

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

extern mod extra;

use std::os;
use std::io::stderr;
use std::io::fs;
use std::io::FileStat;
use std::option::Option;
use std::path::Path;
use extra::arc::Arc;
use extra::future::Future;
use extra::getopts::groups;

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

struct Options {
    all: bool,
    max_depth: Option<uint>,
    total: bool
}

fn du(path: &Path, options_arc: Arc<Options>, depth: uint) -> ~[Arc<FileStat>] {
    let mut stats = ~[];
    let mut futures = ~[];
    let options = options_arc.get();
    let mut my_stat = path.stat();

    for f in fs::readdir(path).move_iter() {
        match f.is_file() {
            true => {
                let stat = f.stat();
                my_stat.size += stat.size;
                if options.all {
                    stats.push(Arc::new(stat))
                }
            }
            false => {
                let oa_clone = options_arc.clone();
                futures.push(do Future::spawn { du(&f, oa_clone, depth + 1) })
            }
        }
    }

    for future in futures.mut_iter() {
        for stat_arc in future.get().move_rev_iter() {
            let stat = stat_arc.get();
            if stat.path.dir_path() == my_stat.path {
                my_stat.size += stat.size;
            }
            if options.max_depth == None || depth < options.max_depth.unwrap() {
                stats.push(stat_arc.clone());
            }
        }
    }

    stats.push(Arc::new(my_stat));

    return stats;
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        // In task
        groups::optflag("a", "all", " write counts for all files, not just directories"),
        // // In main
        // groups::optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage;
        //     although  the apparent  size is usually smaller, it may be larger due to holes
        //     in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like"),
        // In main
        // groups::optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
        //     E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
        //     "SIZE"),
        // // In main
        // groups::optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'"),
        // In main
        groups::optflag("c", "total", "produce a grand total"),
        // In task
        // groups::optflag("D", "dereference-args", "dereference only symlinks that are listed
        //     on the command line"),
        // In main
        // groups::optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
        //                   names specified in file F;
        //                   If F is - then read names from standard input", "F"),
        // // In task
        // groups::optflag("H", "", "equivalent to --dereference-args (-D)"),
        // In main
        groups::optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)"),
        // In main
        groups::optflag("", "si", "like -h, but use powers of 1000 not 1024"),
        // In main
        groups::optflag("k", "", "like --block-size=1K"),
        // // In task
        // groups::optflag("l", "count-links", "count sizes many times if hard linked"),
        // // In main
        // groups::optflag("m", "", "like --block-size=1M"),
        // // In task
        // groups::optflag("L", "dereference", "dereference all symbolic links"),
        // // In task
        // groups::optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
        // // In main
        groups::optflag("0", "null", "end each output line with 0 byte rather than newline"),
        // In main?
        groups::optflag("S", "separate-dirs", "do not include size of subdirectories"),
        // In main
        groups::optflag("s", "summarize", "display only a total for each argument"),
        // // In task
        // groups::optflag("x", "one-file-system", "skip directories on different file systems"),
        // // In task
        // groups::optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
        // // In task
        // groups::optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
        // // In main
        groups::optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N"),
        // // In main
        // groups::optflag("", "time", "show time of the last modification of any file in the
        //     directory, or any of its subdirectories"),
        // // In main
        // groups::optopt("", "time", "show time as WORD instead of modification time:
        //     atime, access, use, ctime or status", "WORD"),
        // // In main
        // groups::optopt("", "time-style", "show times using style STYLE:
        //     full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("du " + VERSION + " - estimate file space usage");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("  {0:s} [OPTION]... --files0-from=F", program);
        println("");
        println(groups::usage("Summarize disk usage of each FILE, recursively for directories.", opts));
        println("Display  values  are  in  units  of  the  first  available  SIZE from
--block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
POSIXLY_CORRECT is set).

SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
ers of 1000).");
        return
    }

    let options = Options{
        all: matches.opt_present("all"),
        max_depth: match (matches.opt_present("summarize"), matches.opt_str("max-depth")) {
            (true, Some(s)) => match from_str::<uint>(s) {
                Some(_) => {
                    println!("du: warning: summarizing conflicts with --max-depth={:s}", s);
                    return
                },
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (true, None) => Some(0),
            (false, Some(s)) => match from_str::<uint>(s) {
                Some(u) => Some(u),
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (false, None) => None
        },
        total: matches.opt_present("total")
    };

    let strs = matches.free.clone();
    let strs = match strs.is_empty() {
        true => ~[~"./"],
        false => strs
    };

    let options_arc = Arc::new(options);

    let mut grand_total = 0;
    for path_str in strs.iter() {
        let path = Path::init(path_str.clone());
        let iter = du(&path, options_arc.clone(), 0).move_iter();
        let (_, len) = iter.size_hint();
        let len = len.unwrap();
        for (index, stat_arc) in iter.enumerate() {
            let stat = stat_arc.get();
            println!("{:<10} {}", stat.size, stat.path.display());
            if options.total && index == (len - 1) {
                // The last element will be the total size of the the path under
                // path_str.  We add it to the grand total.
                grand_total += stat.size;
            }
        }
    }

    if options.total {
        println!("{:<10} total", grand_total);
    }
}
<commit_msg>Add formatting-related logic<commit_after>#[link(name="du", vers="1.0.0", author="Derek Chiang")];

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

extern mod extra;

use std::os;
use std::io::stderr;
use std::io::fs;
use std::io::FileStat;
use std::option::Option;
use std::path::Path;
use extra::arc::Arc;
use extra::future::Future;
use extra::getopts::groups;

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

struct Options {
    all: bool,
    max_depth: Option<uint>,
    total: bool
}

fn du(path: &Path, options_arc: Arc<Options>, depth: uint) -> ~[Arc<FileStat>] {
    let mut stats = ~[];
    let mut futures = ~[];
    let options = options_arc.get();
    let mut my_stat = path.stat();

    for f in fs::readdir(path).move_iter() {
        match f.is_file() {
            true => {
                let stat = f.stat();
                my_stat.size += stat.size;
                if options.all {
                    stats.push(Arc::new(stat))
                }
            }
            false => {
                let oa_clone = options_arc.clone();
                futures.push(do Future::spawn { du(&f, oa_clone, depth + 1) })
            }
        }
    }

    for future in futures.mut_iter() {
        for stat_arc in future.get().move_rev_iter() {
            let stat = stat_arc.get();
            if stat.path.dir_path() == my_stat.path {
                my_stat.size += stat.size;
            }
            if options.max_depth == None || depth < options.max_depth.unwrap() {
                stats.push(stat_arc.clone());
            }
        }
    }

    stats.push(Arc::new(my_stat));

    return stats;
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        // In task
        groups::optflag("a", "all", " write counts for all files, not just directories"),
        // // In main
        // groups::optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage;
        //     although  the apparent  size is usually smaller, it may be larger due to holes
        //     in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like"),
        // In main
        // groups::optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
        //     E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
        //     "SIZE"),
        // // In main
        // groups::optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'"),
        // In main
        groups::optflag("c", "total", "produce a grand total"),
        // In task
        // groups::optflag("D", "dereference-args", "dereference only symlinks that are listed
        //     on the command line"),
        // In main
        // groups::optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
        //                   names specified in file F;
        //                   If F is - then read names from standard input", "F"),
        // // In task
        // groups::optflag("H", "", "equivalent to --dereference-args (-D)"),
        // In main
        groups::optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)"),
        // In main
        groups::optflag("", "si", "like -h, but use powers of 1000 not 1024"),
        // In main
        groups::optflag("k", "", "like --block-size=1K"),
        // // In task
        // groups::optflag("l", "count-links", "count sizes many times if hard linked"),
        // // In main
        groups::optflag("m", "", "like --block-size=1M"),
        // // In task
        // groups::optflag("L", "dereference", "dereference all symbolic links"),
        // // In task
        // groups::optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
        // // In main
        groups::optflag("0", "null", "end each output line with 0 byte rather than newline"),
        // In main?
        groups::optflag("S", "separate-dirs", "do not include size of subdirectories"),
        // In main
        groups::optflag("s", "summarize", "display only a total for each argument"),
        // // In task
        // groups::optflag("x", "one-file-system", "skip directories on different file systems"),
        // // In task
        // groups::optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
        // // In task
        // groups::optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
        // // In main
        groups::optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N"),
        // // In main
        // groups::optflag("", "time", "show time of the last modification of any file in the
        //     directory, or any of its subdirectories"),
        // // In main
        // groups::optopt("", "time", "show time as WORD instead of modification time:
        //     atime, access, use, ctime or status", "WORD"),
        // // In main
        // groups::optopt("", "time-style", "show times using style STYLE:
        //     full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("du " + VERSION + " - estimate file space usage");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("  {0:s} [OPTION]... --files0-from=F", program);
        println("");
        println(groups::usage("Summarize disk usage of each FILE, recursively for directories.", opts));
        println("Display  values  are  in  units  of  the  first  available  SIZE from
--block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
POSIXLY_CORRECT is set).

SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
ers of 1000).");
        return
    }

    let options = Options{
        all: matches.opt_present("all"),
        max_depth: match (matches.opt_present("summarize"), matches.opt_str("max-depth")) {
            (true, Some(s)) => match from_str::<uint>(s) {
                Some(_) => {
                    println!("du: warning: summarizing conflicts with --max-depth={:s}", s);
                    return
                },
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (true, None) => Some(0),
            (false, Some(s)) => match from_str::<uint>(s) {
                Some(u) => Some(u),
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (false, None) => None
        },
        total: matches.opt_present("total")
    };

    let strs = matches.free.clone();
    let strs = match strs.is_empty() {
        true => ~[~"./"],
        false => strs
    };

    let options_arc = Arc::new(options);

    let MB = 1024 * 1024;
    let KB = 1024;
    let convert_size = |size: u64| -> ~str {
        if matches.opt_present("human-readable") {
            if size > MB {
                format!("{:.1f}MB", (size as f64) / (MB as f64))
            } else if size > KB {
                format!("{:.1f}KB", (size as f64) / (KB as f64))
            } else {
                format!("{}B", size)
            }
        } else if matches.opt_present("k") {
            format!("{}", ((size as f64) / (KB as f64)).ceil())
        } else if matches.opt_present("m") {
            format!("{}", ((size as f64) / (MB as f64)).ceil())
        } else {
            format!("{}", ((size as f64) / (KB as f64)).ceil())
        }
    };

    let mut grand_total = 0;
    for path_str in strs.iter() {
        let path = Path::init(path_str.clone());
        let iter = du(&path, options_arc.clone(), 0).move_iter();
        let (_, len) = iter.size_hint();
        let len = len.unwrap();
        for (index, stat_arc) in iter.enumerate() {
            let stat = stat_arc.get();
            println!("{:<10} {}", convert_size(stat.size), stat.path.display());
            if options.total && index == (len - 1) {
                // The last element will be the total size of the the path under
                // path_str.  We add it to the grand total.
                grand_total += stat.size;
            }
        }
    }

    if options.total {
        println!("{:<10} total", convert_size(grand_total));
    }
}
<|endoftext|>


<commit_before>use rand::distributions::{IndependentSample, Range};
use rand;
use gl;
use std;
use shader::Shader;
use shader::ShaderProgram;
use shader::ShaderType;
use super::graphics::vao::VAO;
use super::Miliseconds;
use cgmath;
use cgmath::SquareMatrix;
use cgmath::Matrix4;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Position {
    x: f32,
    y: f32,
    z: f32,
    w: f32
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Velocity {
    x: f32,
    y: f32,
    z: f32,
    w: f32
}

pub struct ParticleSystem {
    particle_pos: [Vec<Position>; 2],
    particle_vel: [Vec<Velocity>; 2],
    draw_shader_program: ShaderProgram,
    vao: VAO,
    now: std::time::Instant,
    gl_handle_pos_buffer: u32,
    active_buffer: usize
}

impl ParticleSystem {
    pub fn new(particle_count: usize) -> ParticleSystem {
        let mut system = ParticleSystem {
            particle_pos: [Vec::with_capacity(particle_count), Vec::with_capacity(particle_count)],
            particle_vel: [Vec::with_capacity(particle_count), Vec::with_capacity(particle_count)],
            draw_shader_program: ShaderProgram::new(),
            vao: VAO::new(),
            now: std::time::Instant::now(),
            gl_handle_pos_buffer: 0,
            active_buffer: 0
        };

        let mut rng = rand::thread_rng();
        let range = Range::new(-1.0, 1.0);

        for i in 0..particle_count {
            let particle = Position {
                x : range.ind_sample(&mut rng),
                y : range.ind_sample(&mut rng),
                z : 0.0, //range.ind_sample(&mut rng),
                w : 0.0
            };

            for i in 0..2 {
                system.particle_pos[i].push(particle);
            }
        }

        system
    }

    pub fn update(&mut self, dt: f64) {

        let active_buffer = self.active_buffer;
        let output_buffer = self.get_output_buffer_index();

        for i in 0..self.particle_pos[0].len() {
            let mut input_y = 0.0;
            {
                let ref input_buffer = &(self.particle_pos[active_buffer]);
                input_y = input_buffer[i].y;
            }

            let ref mut output_buffer = &mut (self.particle_pos[output_buffer]);

            output_buffer[i].y = input_y + (-0.100 * dt) as f32;
        }

    }

    fn get_output_buffer_index(&self) -> usize {
        ((self.active_buffer + 1) % 2) as usize
    }

    fn get_input_buffer_index(&self) -> usize {
        self.active_buffer
    }

    pub fn init_graphics_resources(&mut self) {
        let vertices: Vec<f32> = vec!(
            -0.5, -0.5, 0.0, 0.0,
             0.5, -0.5, 0.0, 0.0,
             0.0,  0.5, 0.0, 0.0
        );

        unsafe {
            //TODO is there another way to do this?
            let memory = std::slice::from_raw_parts(self.particle_pos.as_ptr() as *const f32,
                self.particle_pos.len()*4);
            self.gl_handle_pos_buffer = VAO::create_buffer();
            self.vao.set_buffer(self.gl_handle_pos_buffer, memory, 0, 4*4);
        }

        let mut vertex_shader = Shader::new(ShaderType::Vertex, "shaders/vertex_shader.v.glsl");
        vertex_shader.compile();

        let mut fragment_shader = Shader::new(ShaderType::Fragment, "shaders/pixel_shader.p.glsl");
        fragment_shader.compile();

        let mut geometry_shader = Shader::new(ShaderType::Geometry, "shaders/geometry_shader.g.glsl");
        geometry_shader.compile();

        self.draw_shader_program.attach_shader(&vertex_shader);
        self.draw_shader_program.attach_shader(&fragment_shader);
        self.draw_shader_program.attach_shader(&geometry_shader);
        self.draw_shader_program.link();

    }

    pub fn render(&mut self) {

        self.draw_shader_program.start_use();

        let elapsed = self.now.elapsed().as_milis();
        let colorg = (elapsed % 1000) as f32 / 1000.0f32;
        self.draw_shader_program.set_uniform4f("vtx_color", &[0.3, colorg, 0.3, 1.0]);

        let identity_mtx = cgmath::Matrix4::<f32>::identity();
        self.draw_shader_program.set_uniform_matrix4("view_from_world", identity_mtx.as_ref());
        self.draw_shader_program.set_uniform_matrix4("proj_from_view", identity_mtx.as_ref());

        let buffer_to_draw_index = self.get_output_buffer_index();
        let ref pos_buffer_to_draw = &self.particle_pos[buffer_to_draw_index];
        unsafe {
            //TODO is there another way to do this?
            let memory = std::slice::from_raw_parts(pos_buffer_to_draw.as_ptr() as *const f32,
                pos_buffer_to_draw.len()*4);
            self.vao.set_buffer(self.gl_handle_pos_buffer, memory, 0, 4*4);
        }
        self.vao.bind();

        unsafe {
            gl::DrawArrays(gl::POINTS, 0, pos_buffer_to_draw.len() as i32);
        }

        self.draw_shader_program.stop_use();

        self.active_buffer = (self.active_buffer + 1) % 2;
    }
}<commit_msg>Added compute shader<commit_after>use rand::distributions::{IndependentSample, Range};
use rand;
use gl;
use std;
use shader::Shader;
use shader::ShaderProgram;
use shader::ShaderType;
use super::graphics::vao::VAO;
use super::Miliseconds;
use cgmath;
use cgmath::SquareMatrix;
use cgmath::Matrix4;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Position {
    x: f32,
    y: f32,
    z: f32,
    w: f32
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Velocity {
    x: f32,
    y: f32,
    z: f32,
    w: f32
}

pub struct ParticleSystem {
    particle_pos: [Vec<Position>; 2],
    particle_vel: [Vec<Velocity>; 2],
    draw_shader_program: ShaderProgram,
    compute_shader_program: ShaderProgram,
    vao: VAO,
    now: std::time::Instant,
    gl_handle_pos_buffer: u32,
    active_buffer: usize
}

impl ParticleSystem {
    pub fn new(particle_count: usize) -> ParticleSystem {
        let mut system = ParticleSystem {
            particle_pos: [Vec::with_capacity(particle_count), Vec::with_capacity(particle_count)],
            particle_vel: [Vec::with_capacity(particle_count), Vec::with_capacity(particle_count)],
            draw_shader_program: ShaderProgram::new(),
            compute_shader_program: ShaderProgram::new(),
            vao: VAO::new(),
            now: std::time::Instant::now(),
            gl_handle_pos_buffer: 0,
            active_buffer: 0
        };

        let mut rng = rand::thread_rng();
        let range = Range::new(-1.0, 1.0);

        for i in 0..particle_count {
            let particle = Position {
                x : range.ind_sample(&mut rng),
                y : range.ind_sample(&mut rng),
                z : 0.0, //range.ind_sample(&mut rng),
                w : 0.0
            };

            for i in 0..2 {
                system.particle_pos[i].push(particle);
            }
        }

        system
    }

    pub fn update(&mut self, dt: f64) {

        let active_buffer = self.active_buffer;
        let output_buffer = self.get_output_buffer_index();

        for i in 0..self.particle_pos[0].len() {
            let mut input_y = 0.0;
            {
                let ref input_buffer = &(self.particle_pos[active_buffer]);
                input_y = input_buffer[i].y;
            }

            let ref mut output_buffer = &mut (self.particle_pos[output_buffer]);

            output_buffer[i].y = input_y + (-0.100 * dt) as f32;
        }

    }

    fn get_output_buffer_index(&self) -> usize {
        ((self.active_buffer + 1) % 2) as usize
    }

    fn get_input_buffer_index(&self) -> usize {
        self.active_buffer
    }

    pub fn init_graphics_resources(&mut self) {
        unsafe {
            //TODO is there another way to do this?
            let memory = std::slice::from_raw_parts(self.particle_pos.as_ptr() as *const f32,
                self.particle_pos.len()*4);
            self.gl_handle_pos_buffer = VAO::create_buffer();
            self.vao.set_buffer(self.gl_handle_pos_buffer, memory, 0, 4*4);
        }

        let mut vertex_shader = Shader::new(ShaderType::Vertex, "shaders/vertex_shader.v.glsl");
        vertex_shader.compile();

        let mut fragment_shader = Shader::new(ShaderType::Fragment, "shaders/pixel_shader.p.glsl");
        fragment_shader.compile();

        let mut geometry_shader = Shader::new(ShaderType::Geometry, "shaders/geometry_shader.g.glsl");
        geometry_shader.compile();

        self.draw_shader_program.attach_shader(&vertex_shader);
        self.draw_shader_program.attach_shader(&fragment_shader);
        self.draw_shader_program.attach_shader(&geometry_shader);
        self.draw_shader_program.link();

        let mut compute_shader = Shader::new(ShaderType::Compute, "shaders/compute_shader.c.glsl");
        compute_shader.compile();

        self.compute_shader_program.attach_shader(&compute_shader);
        self.compute_shader_program.link();
    }

    pub fn render(&mut self) {

        self.draw_shader_program.start_use();

        let elapsed = self.now.elapsed().as_milis();
        let colorg = (elapsed % 1000) as f32 / 1000.0f32;
        self.draw_shader_program.set_uniform4f("vtx_color", &[0.3, colorg, 0.3, 1.0]);

        let identity_mtx = cgmath::Matrix4::<f32>::identity();
        self.draw_shader_program.set_uniform_matrix4("view_from_world", identity_mtx.as_ref());
        self.draw_shader_program.set_uniform_matrix4("proj_from_view", identity_mtx.as_ref());

        let buffer_to_draw_index = self.get_output_buffer_index();
        let ref pos_buffer_to_draw = &self.particle_pos[buffer_to_draw_index];
        unsafe {
            //TODO is there another way to do this?
            let memory = std::slice::from_raw_parts(pos_buffer_to_draw.as_ptr() as *const f32,
                pos_buffer_to_draw.len()*4);
            self.vao.set_buffer(self.gl_handle_pos_buffer, memory, 0, 4*4);
        }
        self.vao.bind();

        unsafe {
            gl::DrawArrays(gl::POINTS, 0, pos_buffer_to_draw.len() as i32);
        }

        self.draw_shader_program.stop_use();

        self.active_buffer = (self.active_buffer + 1) % 2;
    }
}<|endoftext|>


<commit_before>//! Implements a simple parser and extractor for a Unix Archive.
//!
//! There are two "common" formats: BSD and SysV
//!
//! This crate currently only implements the SysV version, which essentially postfixes all
//! names in the archive with a / as a sigil for the end of the name, and uses a special symbol
//! index for looking up symbols faster.

use byteorder::{BigEndian, ReadBytesExt};

use elf::strtab;

use std::io::{self, Read, Seek, Cursor};
use std::io::SeekFrom::{Start, Current};
use std::usize;
use std::collections::HashMap;

pub const SIZEOF_MAGIC: usize = 8;
/// The magic number of a Unix Archive
pub const MAGIC: &'static [u8; SIZEOF_MAGIC] = b"!<arch>\x0A";

const SIZEOF_FILE_IDENTIFER: usize = 16;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
/// A Unix Archive File Header. Meta data for the file which follows exactly after.  All data
/// is right-padded with spaces ASCII `0x20`.
///
/// > Each archive file member begins on an even byte boundary; a newline is inserted between files
/// > if necessary. Nevertheless, the size given reflects the actual size of the file exclusive
/// > of padding.
// TODO: serialize more values here
pub struct FileHeader {
    /// The identifier, or name for this file.
    pub identifier: String,
    /// The timestamp for when this file was last modified. Base 10 number
    pub timestamp: [u8; 12],
    /// The file's owner's id. Base 10 string number
    pub owner_id: [u8; 6],
    /// The file's group id. Base 10 string number
    pub group_id: [u8; 6],
    /// The file's permissions mode. Base 8 number number
    pub mode: [u8; 8],
    /// The size of this file. Base 10 string number
    pub size: usize,
    /// The file header's terminator, always `0x60 0x0A`
    pub terminator: [u8; 2],
}

const SIZEOF_FILE_SIZE: usize = 10;
pub const SIZEOF_FILEHEADER: usize = SIZEOF_FILE_IDENTIFER + 12 + 6 + 6 + 8 + SIZEOF_FILE_SIZE + 2;

impl FileHeader {
    pub fn parse<R: Read + Seek>(cursor: &mut R) -> io::Result<FileHeader> {
        let mut file_identifier = vec![0u8; SIZEOF_FILE_IDENTIFER];
        try!(cursor.read_exact(&mut file_identifier));
        let file_identifier = unsafe { String::from_utf8_unchecked(file_identifier) };
        let mut file_modification_timestamp = [0u8; 12];
        try!(cursor.read(&mut file_modification_timestamp));
        let mut owner_id = [0u8; 6];
        try!(cursor.read(&mut owner_id));
        let mut group_id = [0u8; 6];
        try!(cursor.read(&mut group_id));
        let mut file_mode = [0u8; 8];
        try!(cursor.read(&mut file_mode));
        let mut file_size = [0u8; SIZEOF_FILE_SIZE];
        try!(cursor.read(&mut file_size));
        let mut terminator = [0u8; 2];
        try!(cursor.read(&mut terminator));
        let string = unsafe { ::std::str::from_utf8_unchecked(&file_size) };
        let file_size = match usize::from_str_radix(string.trim_right(), 10) {
            Ok(file_size) => file_size,
            Err(err) => return io_error!("Err: {:?} Bad file_size {:?}", err, &file_size),
        };
        Ok(FileHeader {
            identifier: file_identifier,
            timestamp: file_modification_timestamp.clone(),
            owner_id: owner_id.clone(),
            group_id: group_id.clone(),
            mode: file_mode.clone(),
            size: file_size,
            terminator: terminator,
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
/// Represents a single file entry in the archive
pub struct File {
    /// The parsed file header
    pub header: FileHeader,
    /// File offset from the beginning of the archive to where the file begins
    pub data_offset: u64,
}

impl File {
    pub fn parse<R: Read + Seek>(cursor: &mut R) -> io::Result<File> {
        let header = try!(FileHeader::parse(cursor));
        let data_offset = try!(cursor.seek(Current(0)));
        try!(cursor.seek(Current(header.size as i64)));
        Ok(File {
            header: header,
            data_offset: data_offset,
        })
    }
}

#[derive(Debug, Default)]
/// The special index member signified by the name `"/"`.
/// The data element contains a list of symbol indexes and symbol names, giving their offsets
/// into the archive for a given name.
// TODO: make this into a hashmap from string -> (file_name, offset) indexes?
pub struct Index {
    /// Big Endian number of symbol_indexes and strings
    pub size: usize,
    /// Big Endian u32 index into the archive for this symbol (index in array is the index into the string table)
    pub symbol_indexes: Vec<u32>,
    /// Set of zero-terminated strings indexed by above. Number of strings = `self.size`
    pub strtab: strtab::Strtab<'static>,
}

/// SysV Archive Variant Symbol Lookup Table "Magic" Name
pub const SYMBOL_LOOKUP_MAGIC: &'static [u8; SIZEOF_FILE_IDENTIFER] = b"/               ";
pub const SYMBOL_LOOKUP_NAME: &'static str = "/               ";

impl Index {
    pub fn parse<'c, R: Read + Seek>(cursor: &'c mut R, size: usize) -> io::Result<Index> {
        let sizeof_table = try!(cursor.read_u32::<BigEndian>()) as usize;
        let mut indexes = Vec::with_capacity(sizeof_table);
        for _ in 0..sizeof_table {
            indexes.push(try!(cursor.read_u32::<BigEndian>()));
        }
        let sizeof_strtab = size - ((sizeof_table * 4) + 4);
        let offset = try!(cursor.seek(Current(0)));
        let strtab = try!(strtab::Strtab::parse(cursor, offset as usize, sizeof_strtab));
        Ok (Index {
            size: sizeof_table,
            symbol_indexes: indexes,
            strtab: strtab,
        })
    }
}

#[derive(Debug)]
pub struct Archive {
    pub index: Index,
    files: HashMap<String, File>,
}

impl Archive {
    pub fn parse<R: Read + Seek>(mut cursor: &mut R, _size: usize) -> io::Result<Archive> {
        try!(cursor.seek(Start(0)));
        let mut magic = [0; SIZEOF_MAGIC];
        try!(cursor.read_exact(&mut magic));
        if &magic != MAGIC {
            return io_error!("Invalid Archive magic number: {:?}", &magic);
        }
        let mut files = HashMap::new();
        loop {
            match File::parse(&mut cursor) {
                Ok(file) => {
                    files.insert(file.header.identifier.to_owned(), file);
                }
                Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => {
                    break;
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }

        let mut index = Index::default();
        if let Some(file) = files.get(SYMBOL_LOOKUP_NAME) {
            let mut data = vec![0u8; file.header.size];
            try!(cursor.seek(Start(file.data_offset)));
            try!(cursor.read_exact(&mut data));
            let mut data = Cursor::new(&data);
            index = try!(Index::parse(&mut data, file.header.size));
        }
        let archive = Archive {
            index: index,
            files: files,
        };
        Ok(archive)
    }

    pub fn get (&self, member: &str) -> Option<&File> {
        self.files.get(member)
    }

    pub fn extract<R: Read + Seek> (&self, member: &str, cursor: &mut R) -> io::Result<Vec<u8>> {
        if let Some(file) = self.get(member) {
            let mut bytes = vec![0u8; file.header.size];
            try!(cursor.seek(Start(file.data_offset)));
            try!(cursor.read_exact(&mut bytes));
            Ok(bytes)
        } else {
            return io_error!(format!("Error: cannot extract member {}, not found", member));
        }
    }
}

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

    #[test]
    fn parse_file_header() {
        let file_header: [u8; SIZEOF_FILEHEADER] = [0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38,
                                                    0x32, 0x34, 0x34, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x60, 0x0a];
        let mut cursor = Cursor::new(&file_header[..]);
        match FileHeader::parse(&mut cursor) {
            Err(_) => assert!(false),
            Ok(file_header2) => {
                let file_header = FileHeader { identifier: "/               ".to_owned(),
                    timestamp: [48, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
                    owner_id: [48, 32, 32, 32, 32, 32],
                    group_id: [48, 32, 32, 32, 32, 32],
                    mode: [48, 32, 32, 32, 32, 32, 32, 32],
                    size: 8244, terminator:
                    [96, 10] };
                assert_eq!(file_header, file_header2)
            }
        }
    }

    #[test]
    fn parse_archive() {
        let crt1a: Vec<u8> = include!("../../crt1a.rs");
        let mut cursor = Cursor::new(&crt1a);
        match Archive::parse(&mut cursor, crt1a.len()) {
            Ok(archive) => {
                if let Some(file) = archive.get("crt1.o/         ") {
                    assert_eq!(file.data_offset, 194);
                    assert_eq!(file.header.size, 1928)
                } else {
                    assert!(false)
                }
            },
            Err(_) => assert!(false),
        };
    }
}
<commit_msg>archive: add nice table to docs for FileHeader<commit_after>//! Implements a simple parser and extractor for a Unix Archive.
//!
//! There are two "common" formats: BSD and SysV
//!
//! This crate currently only implements the SysV version, which essentially postfixes all
//! names in the archive with a / as a sigil for the end of the name, and uses a special symbol
//! index for looking up symbols faster.

use byteorder::{BigEndian, ReadBytesExt};

use elf::strtab;

use std::io::{self, Read, Seek, Cursor};
use std::io::SeekFrom::{Start, Current};
use std::usize;
use std::collections::HashMap;

pub const SIZEOF_MAGIC: usize = 8;
/// The magic number of a Unix Archive
pub const MAGIC: &'static [u8; SIZEOF_MAGIC] = b"!<arch>\x0A";

const SIZEOF_FILE_IDENTIFER: usize = 16;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
/// A Unix Archive File Header - meta data for the file/byte blob/whatever that follows exactly after.
/// All data is right-padded with spaces ASCII `0x20`. The Binary layout is as follows:
///
/// |Offset|Length|Name                       |Format |
/// |:-----|:-----|:--------------------------|:------|
/// |0     |16    |File identifier            |ASCII  |
/// |16    |12    |File modification timestamp|Decimal|
/// |28    |6     |Owner ID                   |Decimal|
/// |34    |6     |Group ID                   |Decimal|
/// |40    |8     |File mode                  |Octal  |
/// |48    |10    |Filesize in bytes          |Decimal|
/// |58    |2     |Ending characters          |`0x60 0x0A`|
///
/// Byte alignment is according to the following:
/// > Each archive file member begins on an even byte boundary; a newline is inserted between files
/// > if necessary. Nevertheless, the size given reflects the actual size of the file exclusive
/// > of padding.
// TODO: serialize more values here
pub struct FileHeader {
    /// The identifier, or name for this file.
    pub identifier: String,
    /// The timestamp for when this file was last modified. Base 10 number
    pub timestamp: [u8; 12],
    /// The file's owner's id. Base 10 string number
    pub owner_id: [u8; 6],
    /// The file's group id. Base 10 string number
    pub group_id: [u8; 6],
    /// The file's permissions mode. Base 8 number number
    pub mode: [u8; 8],
    /// The size of this file. Base 10 string number
    pub size: usize,
    /// The file header's terminator, always `0x60 0x0A`
    pub terminator: [u8; 2],
}

const SIZEOF_FILE_SIZE: usize = 10;
pub const SIZEOF_FILEHEADER: usize = SIZEOF_FILE_IDENTIFER + 12 + 6 + 6 + 8 + SIZEOF_FILE_SIZE + 2;

impl FileHeader {
    pub fn parse<R: Read + Seek>(cursor: &mut R) -> io::Result<FileHeader> {
        let mut file_identifier = vec![0u8; SIZEOF_FILE_IDENTIFER];
        try!(cursor.read_exact(&mut file_identifier));
        let file_identifier = unsafe { String::from_utf8_unchecked(file_identifier) };
        let mut file_modification_timestamp = [0u8; 12];
        try!(cursor.read(&mut file_modification_timestamp));
        let mut owner_id = [0u8; 6];
        try!(cursor.read(&mut owner_id));
        let mut group_id = [0u8; 6];
        try!(cursor.read(&mut group_id));
        let mut file_mode = [0u8; 8];
        try!(cursor.read(&mut file_mode));
        let mut file_size = [0u8; SIZEOF_FILE_SIZE];
        try!(cursor.read(&mut file_size));
        let mut terminator = [0u8; 2];
        try!(cursor.read(&mut terminator));
        let string = unsafe { ::std::str::from_utf8_unchecked(&file_size) };
        let file_size = match usize::from_str_radix(string.trim_right(), 10) {
            Ok(file_size) => file_size,
            Err(err) => return io_error!("Err: {:?} Bad file_size {:?}", err, &file_size),
        };
        Ok(FileHeader {
            identifier: file_identifier,
            timestamp: file_modification_timestamp.clone(),
            owner_id: owner_id.clone(),
            group_id: group_id.clone(),
            mode: file_mode.clone(),
            size: file_size,
            terminator: terminator,
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
/// Represents a single file entry in the archive
pub struct File {
    /// The parsed file header
    pub header: FileHeader,
    /// File offset from the beginning of the archive to where the file begins
    pub data_offset: u64,
}

impl File {
    pub fn parse<R: Read + Seek>(cursor: &mut R) -> io::Result<File> {
        let header = try!(FileHeader::parse(cursor));
        let data_offset = try!(cursor.seek(Current(0)));
        try!(cursor.seek(Current(header.size as i64)));
        Ok(File {
            header: header,
            data_offset: data_offset,
        })
    }
}

#[derive(Debug, Default)]
/// The special index member signified by the name `"/"`.
/// The data element contains a list of symbol indexes and symbol names, giving their offsets
/// into the archive for a given name.
// TODO: make this into a hashmap from string -> (file_name, offset) indexes?
pub struct Index {
    /// Big Endian number of symbol_indexes and strings
    pub size: usize,
    /// Big Endian u32 index into the archive for this symbol (index in array is the index into the string table)
    pub symbol_indexes: Vec<u32>,
    /// Set of zero-terminated strings indexed by above. Number of strings = `self.size`
    pub strtab: strtab::Strtab<'static>,
}

/// SysV Archive Variant Symbol Lookup Table "Magic" Name
pub const SYMBOL_LOOKUP_MAGIC: &'static [u8; SIZEOF_FILE_IDENTIFER] = b"/               ";
pub const SYMBOL_LOOKUP_NAME: &'static str = "/               ";

impl Index {
    pub fn parse<'c, R: Read + Seek>(cursor: &'c mut R, size: usize) -> io::Result<Index> {
        let sizeof_table = try!(cursor.read_u32::<BigEndian>()) as usize;
        let mut indexes = Vec::with_capacity(sizeof_table);
        for _ in 0..sizeof_table {
            indexes.push(try!(cursor.read_u32::<BigEndian>()));
        }
        let sizeof_strtab = size - ((sizeof_table * 4) + 4);
        let offset = try!(cursor.seek(Current(0)));
        let strtab = try!(strtab::Strtab::parse(cursor, offset as usize, sizeof_strtab));
        Ok (Index {
            size: sizeof_table,
            symbol_indexes: indexes,
            strtab: strtab,
        })
    }
}

#[derive(Debug)]
pub struct Archive {
    pub index: Index,
    files: HashMap<String, File>,
}

impl Archive {
    pub fn parse<R: Read + Seek>(mut cursor: &mut R, _size: usize) -> io::Result<Archive> {
        try!(cursor.seek(Start(0)));
        let mut magic = [0; SIZEOF_MAGIC];
        try!(cursor.read_exact(&mut magic));
        if &magic != MAGIC {
            return io_error!("Invalid Archive magic number: {:?}", &magic);
        }
        let mut files = HashMap::new();
        loop {
            match File::parse(&mut cursor) {
                Ok(file) => {
                    files.insert(file.header.identifier.to_owned(), file);
                }
                Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => {
                    break;
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }

        let mut index = Index::default();
        if let Some(file) = files.get(SYMBOL_LOOKUP_NAME) {
            let mut data = vec![0u8; file.header.size];
            try!(cursor.seek(Start(file.data_offset)));
            try!(cursor.read_exact(&mut data));
            let mut data = Cursor::new(&data);
            index = try!(Index::parse(&mut data, file.header.size));
        }
        let archive = Archive {
            index: index,
            files: files,
        };
        Ok(archive)
    }

    pub fn get (&self, member: &str) -> Option<&File> {
        self.files.get(member)
    }

    pub fn extract<R: Read + Seek> (&self, member: &str, cursor: &mut R) -> io::Result<Vec<u8>> {
        if let Some(file) = self.get(member) {
            let mut bytes = vec![0u8; file.header.size];
            try!(cursor.seek(Start(file.data_offset)));
            try!(cursor.read_exact(&mut bytes));
            Ok(bytes)
        } else {
            return io_error!(format!("Error: cannot extract member {}, not found", member));
        }
    }
}

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

    #[test]
    fn parse_file_header() {
        let file_header: [u8; SIZEOF_FILEHEADER] = [0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                                    0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20,
                                                    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38,
                                                    0x32, 0x34, 0x34, 0x20, 0x20, 0x20, 0x20,
                                                    0x20, 0x20, 0x60, 0x0a];
        let mut cursor = Cursor::new(&file_header[..]);
        match FileHeader::parse(&mut cursor) {
            Err(_) => assert!(false),
            Ok(file_header2) => {
                let file_header = FileHeader { identifier: "/               ".to_owned(),
                    timestamp: [48, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
                    owner_id: [48, 32, 32, 32, 32, 32],
                    group_id: [48, 32, 32, 32, 32, 32],
                    mode: [48, 32, 32, 32, 32, 32, 32, 32],
                    size: 8244, terminator:
                    [96, 10] };
                assert_eq!(file_header, file_header2)
            }
        }
    }

    #[test]
    fn parse_archive() {
        let crt1a: Vec<u8> = include!("../../crt1a.rs");
        let mut cursor = Cursor::new(&crt1a);
        match Archive::parse(&mut cursor, crt1a.len()) {
            Ok(archive) => {
                if let Some(file) = archive.get("crt1.o/         ") {
                    assert_eq!(file.data_offset, 194);
                    assert_eq!(file.header.size, 1928)
                } else {
                    assert!(false)
                }
            },
            Err(_) => assert!(false),
        };
    }
}
<|endoftext|>


<commit_before>use std::io;

use buffer::Cell;
use layout::Rect;

#[cfg(feature = "rustbox")]
mod rustbox;
#[cfg(feature = "rustbox")]
pub use self::rustbox::RustboxBackend;

#[cfg(feature = "termion")]
mod termion;
#[cfg(feature = "termion")]
pub use self::termion::{MouseBackend, RawBackend, TermionBackend};

pub trait Backend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
    where
        I: Iterator<Item = (u16, u16, &'a Cell)>;
    fn hide_cursor(&mut self) -> Result<(), io::Error>;
    fn show_cursor(&mut self) -> Result<(), io::Error>;
    fn clear(&mut self) -> Result<(), io::Error>;
    fn size(&self) -> Result<Rect, io::Error>;
    fn flush(&mut self) -> Result<(), io::Error>;
}
<commit_msg>Export AlternateScreenBackend.<commit_after>use std::io;

use buffer::Cell;
use layout::Rect;

#[cfg(feature = "rustbox")]
mod rustbox;
#[cfg(feature = "rustbox")]
pub use self::rustbox::RustboxBackend;

#[cfg(feature = "termion")]
mod termion;
#[cfg(feature = "termion")]
pub use self::termion::{MouseBackend, RawBackend, TermionBackend, AlternateScreenBackend};

pub trait Backend {
    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
    where
        I: Iterator<Item = (u16, u16, &'a Cell)>;
    fn hide_cursor(&mut self) -> Result<(), io::Error>;
    fn show_cursor(&mut self) -> Result<(), io::Error>;
    fn clear(&mut self) -> Result<(), io::Error>;
    fn size(&self) -> Result<Rect, io::Error>;
    fn flush(&mut self) -> Result<(), io::Error>;
}
<|endoftext|>