<commit_before>//! The <filter> node.
use std::cell::Cell;

use libc;

use attributes::Attribute;
use coord_units::CoordUnits;
use handle::RsvgHandle;
use length::{LengthDir, RsvgLength};
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgNode};
use parsers::{parse, Parse};
use property_bag::PropertyBag;

/// The <filter> node.
pub struct NodeFilter {
    pub x: Cell<RsvgLength>,
    pub y: Cell<RsvgLength>,
    pub width: Cell<RsvgLength>,
    pub height: Cell<RsvgLength>,
    pub filterunits: Cell<CoordUnits>,
    pub primitiveunits: Cell<CoordUnits>,
}

impl NodeFilter {
    /// Constructs a new `NodeFilter` with default properties.
    #[inline]
    fn new() -> Self {
        Self {
            x: Cell::new(RsvgLength::parse("-10%", LengthDir::Horizontal).unwrap()),
            y: Cell::new(RsvgLength::parse("-10%", LengthDir::Vertical).unwrap()),
            width: Cell::new(RsvgLength::parse("120%", LengthDir::Horizontal).unwrap()),
            height: Cell::new(RsvgLength::parse("120%", LengthDir::Vertical).unwrap()),
            filterunits: Cell::new(CoordUnits::ObjectBoundingBox),
            primitiveunits: Cell::new(CoordUnits::UserSpaceOnUse),
        }
    }
}

impl NodeTrait for NodeFilter {
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal, None)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical, None)?),
                Attribute::Width => {
                    self.width
                        .set(parse("width", value, LengthDir::Horizontal, None)?)
                }
                Attribute::Height => {
                    self.height
                        .set(parse("height", value, LengthDir::Vertical, None)?)
                }
                Attribute::FilterUnits => {
                    self.filterunits.set(parse("filterunits", value, (), None)?)
                }
                Attribute::PrimitiveUnits => {
                    self.primitiveunits
                        .set(parse("primitiveunits", value, (), None)?)
                }
                _ => (),
            }
        }

        Ok(())
    }
}

#[no_mangle]
pub extern "C" fn rsvg_new_filter(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Filter, raw_parent, Box::new(NodeFilter::new()))
}
<commit_msg>Use camelCase filterUnits and primitiveUnits<commit_after>//! The <filter> node.
use std::cell::Cell;

use libc;

use attributes::Attribute;
use coord_units::CoordUnits;
use handle::RsvgHandle;
use length::{LengthDir, RsvgLength};
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgNode};
use parsers::{parse, Parse};
use property_bag::PropertyBag;

/// The <filter> node.
pub struct NodeFilter {
    pub x: Cell<RsvgLength>,
    pub y: Cell<RsvgLength>,
    pub width: Cell<RsvgLength>,
    pub height: Cell<RsvgLength>,
    pub filterunits: Cell<CoordUnits>,
    pub primitiveunits: Cell<CoordUnits>,
}

impl NodeFilter {
    /// Constructs a new `NodeFilter` with default properties.
    #[inline]
    fn new() -> Self {
        Self {
            x: Cell::new(RsvgLength::parse("-10%", LengthDir::Horizontal).unwrap()),
            y: Cell::new(RsvgLength::parse("-10%", LengthDir::Vertical).unwrap()),
            width: Cell::new(RsvgLength::parse("120%", LengthDir::Horizontal).unwrap()),
            height: Cell::new(RsvgLength::parse("120%", LengthDir::Vertical).unwrap()),
            filterunits: Cell::new(CoordUnits::ObjectBoundingBox),
            primitiveunits: Cell::new(CoordUnits::UserSpaceOnUse),
        }
    }
}

impl NodeTrait for NodeFilter {
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::X => self.x.set(parse("x", value, LengthDir::Horizontal, None)?),
                Attribute::Y => self.y.set(parse("y", value, LengthDir::Vertical, None)?),
                Attribute::Width => {
                    self.width
                        .set(parse("width", value, LengthDir::Horizontal, None)?)
                }
                Attribute::Height => {
                    self.height
                        .set(parse("height", value, LengthDir::Vertical, None)?)
                }
                Attribute::FilterUnits => {
                    self.filterunits.set(parse("filterUnits", value, (), None)?)
                }
                Attribute::PrimitiveUnits => {
                    self.primitiveunits
                        .set(parse("primitiveUnits", value, (), None)?)
                }
                _ => (),
            }
        }

        Ok(())
    }
}

#[no_mangle]
pub extern "C" fn rsvg_new_filter(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Filter, raw_parent, Box::new(NodeFilter::new()))
}
<|endoftext|>


<commit_before>use libc;

use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::slice;
use std::str::{self, FromStr};

use attributes::Attribute;

pub struct PropertyBag<'a>(Vec<(Attribute, &'a CStr)>);

pub struct OwnedPropertyBag(Vec<(Attribute, CString)>);

pub struct PropertyBagIter<'a>(PropertyBagCStrIter<'a>);

pub struct PropertyBagCStrIter<'a>(slice::Iter<'a, (Attribute, &'a CStr)>);

trait Utf8CStrToStr {
    fn to_str_utf8(&self) -> &str;
}

impl Utf8CStrToStr for CStr {
    fn to_str_utf8(&self) -> &str {
        // We can *only* do this when the CStr comes from a C string that was validated
        // as UTF-8 on the C side of things.  In our case, the C strings from libxml2 and
        // are valid UTF-8.
        unsafe { str::from_utf8_unchecked(self.to_bytes()) }
    }
}

impl<'a> PropertyBag<'a> {
    /// Creates an iterable `PropertyBag` from a C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's callback for "element start"
    /// gets passed a `xmlChar **` of attribute/value pairs.  Even indices
    /// in the array are pointers to attribute names; odd indices are
    /// pointers to attribute values.  The array terminates with a NULL
    /// element in an even index.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `pairs` is a valid pointer, or NULL for an empty array
    ///
    /// * `pairs` has key/value pairs and is NULL terminated
    ///
    /// * Both keys and values are valid UTF-8, nul-terminated C strings
    ///
    /// The lifetime of the `PropertyBag` should be considered the same as the lifetime of the
    /// `pairs` array, as the property bag does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_key_value_pairs(pairs: *const *const libc::c_char) -> PropertyBag<'a> {
        let mut array = Vec::new();

        if !pairs.is_null() {
            let mut i = 0;
            loop {
                let key = *pairs.offset(i);
                if !key.is_null() {
                    let val = *pairs.offset(i + 1);
                    assert!(!val.is_null());

                    let key_str = CStr::from_ptr(key);
                    let val_str = CStr::from_ptr(val);

                    // We silently drop unknown attributes.  New attributes should be added in
                    // build.rs.
                    if let Ok(attr) = Attribute::from_str(key_str.to_str_utf8()) {
                        array.push((attr, val_str));
                    }
                } else {
                    break;
                }

                i += 2;
            }
        }

        PropertyBag(array)
    }

    pub fn from_owned(owned: &OwnedPropertyBag) -> PropertyBag<'_> {
        let mut array = Vec::new();

        for &(a, ref v) in &owned.0 {
            array.push((a, v.deref()));
        }

        PropertyBag(array)
    }

    pub fn to_owned(&self) -> OwnedPropertyBag {
        let mut array = Vec::<(Attribute, CString)>::new();

        for &(a, v) in &self.0 {
            array.push((a, (*v).to_owned()));
        }

        OwnedPropertyBag(array)
    }

    pub fn ffi(&self) -> *const PropertyBag<'_> {
        self as *const PropertyBag<'_>
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> PropertyBagIter<'_> {
        PropertyBagIter(self.cstr_iter())
    }

    pub fn cstr_iter(&self) -> PropertyBagCStrIter<'_> {
        PropertyBagCStrIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (Attribute, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(a, v)| (a, v.to_str_utf8()))
    }
}

impl<'a> Iterator for PropertyBagCStrIter<'a> {
    type Item = (Attribute, &'a CStr);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|&(a, v)| (a, v))
    }
}

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

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_key_value_pairs(ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_iters() {
        let pairs = [
            CString::new("rx").unwrap(),
            CString::new("1").unwrap(),
            CString::new("ry").unwrap(),
            CString::new("2").unwrap(),
        ];

        let mut v = Vec::new();

        for x in &pairs {
            v.push(x.as_ptr() as *const libc::c_char);
        }

        v.push(ptr::null());

        let pbag = unsafe { PropertyBag::new_from_key_value_pairs(v.as_ptr()) };

        let mut had_rx: bool = false;
        let mut had_ry: bool = false;

        for (a, v) in pbag.iter() {
            match a {
                Attribute::Rx => {
                    assert!(v == "1");
                    had_rx = true;
                }
                Attribute::Ry => {
                    assert!(v == "2");
                    had_ry = true;
                }
                _ => unreachable!()
            }
        }

        assert!(had_rx);
        assert!(had_ry);
    }
}
<commit_msg>property_bag: remove ffi leftovers<commit_after>use libc;

use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::slice;
use std::str::{self, FromStr};

use attributes::Attribute;

pub struct PropertyBag<'a>(Vec<(Attribute, &'a CStr)>);

pub struct OwnedPropertyBag(Vec<(Attribute, CString)>);

pub struct PropertyBagIter<'a>(slice::Iter<'a, (Attribute, &'a CStr)>);

trait Utf8CStrToStr {
    fn to_str_utf8(&self) -> &str;
}

impl Utf8CStrToStr for CStr {
    fn to_str_utf8(&self) -> &str {
        // We can *only* do this when the CStr comes from a C string that was validated
        // as UTF-8 on the C side of things.  In our case, the C strings from libxml2 and
        // are valid UTF-8.
        unsafe { str::from_utf8_unchecked(self.to_bytes()) }
    }
}

impl<'a> PropertyBag<'a> {
    /// Creates an iterable `PropertyBag` from a C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's callback for "element start"
    /// gets passed a `xmlChar **` of attribute/value pairs.  Even indices
    /// in the array are pointers to attribute names; odd indices are
    /// pointers to attribute values.  The array terminates with a NULL
    /// element in an even index.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `pairs` is a valid pointer, or NULL for an empty array
    ///
    /// * `pairs` has key/value pairs and is NULL terminated
    ///
    /// * Both keys and values are valid UTF-8, nul-terminated C strings
    ///
    /// The lifetime of the `PropertyBag` should be considered the same as the lifetime of the
    /// `pairs` array, as the property bag does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_key_value_pairs(pairs: *const *const libc::c_char) -> PropertyBag<'a> {
        let mut array = Vec::new();

        if !pairs.is_null() {
            let mut i = 0;
            loop {
                let key = *pairs.offset(i);
                if !key.is_null() {
                    let val = *pairs.offset(i + 1);
                    assert!(!val.is_null());

                    let key_str = CStr::from_ptr(key);
                    let val_str = CStr::from_ptr(val);

                    // We silently drop unknown attributes.  New attributes should be added in
                    // build.rs.
                    if let Ok(attr) = Attribute::from_str(key_str.to_str_utf8()) {
                        array.push((attr, val_str));
                    }
                } else {
                    break;
                }

                i += 2;
            }
        }

        PropertyBag(array)
    }

    pub fn from_owned(owned: &OwnedPropertyBag) -> PropertyBag<'_> {
        let mut array = Vec::new();

        for &(a, ref v) in &owned.0 {
            array.push((a, v.deref()));
        }

        PropertyBag(array)
    }

    pub fn to_owned(&self) -> OwnedPropertyBag {
        let mut array = Vec::<(Attribute, CString)>::new();

        for &(a, v) in &self.0 {
            array.push((a, (*v).to_owned()));
        }

        OwnedPropertyBag(array)
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> PropertyBagIter<'_> {
        PropertyBagIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (Attribute, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|&(a, v)| (a, v.to_str_utf8()))
    }
}

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

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_key_value_pairs(ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_iters() {
        let pairs = [
            CString::new("rx").unwrap(),
            CString::new("1").unwrap(),
            CString::new("ry").unwrap(),
            CString::new("2").unwrap(),
        ];

        let mut v = Vec::new();

        for x in &pairs {
            v.push(x.as_ptr() as *const libc::c_char);
        }

        v.push(ptr::null());

        let pbag = unsafe { PropertyBag::new_from_key_value_pairs(v.as_ptr()) };

        let mut had_rx: bool = false;
        let mut had_ry: bool = false;

        for (a, v) in pbag.iter() {
            match a {
                Attribute::Rx => {
                    assert!(v == "1");
                    had_rx = true;
                }
                Attribute::Ry => {
                    assert!(v == "2");
                    had_ry = true;
                }
                _ => unreachable!(),
            }
        }

        assert!(had_rx);
        assert!(had_ry);
    }
}
<|endoftext|>


<commit_before>use ruma_api::ruma_api;
use ruma_identifiers::UserId;

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Foo;

ruma_api! {
    metadata: {
        description: "Does something.",
        method: POST,
        name: "my_endpoint",
        path: "/_matrix/foo/:bar/",
        rate_limited: false,
        requires_authentication: false,
    }

    request: {
        #[ruma_api(body)]
        pub q2: Foo,

        #[ruma_api(path)]
        pub bar: String,

        #[ruma_api(query)]
        pub baz: UserId,

        #[ruma_api(header = CONTENT_TYPE)]
        pub world: String,
    }

    response: {
        #[ruma_api(raw_body)]
        pub q2: Vec<u8>,

        #[ruma_api(header = CONTENT_TYPE)]
        pub world: String,
    }
}

mod raw_body_request {
    use ruma_api::ruma_api;
    use ruma_identifiers::UserId;

    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
    pub struct Foo;

    ruma_api! {
        metadata: {
            description: "Does something.",
            method: POST,
            name: "my_endpoint",
            path: "/_matrix/foo/:bar/",
            rate_limited: false,
            requires_authentication: false,
        }

        request: {
            #[ruma_api(raw_body)]
            pub q2: Vec<u8>,

            #[ruma_api(path)]
            pub bar: String,

            #[ruma_api(query)]
            pub baz: UserId,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }

        response: {
            #[ruma_api(body)]
            pub q2: Foo,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }
    }
}

mod plain {
    use ruma_api::ruma_api;
    use ruma_identifiers::UserId;

    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
    pub struct Foo;

    ruma_api! {
        metadata: {
            description: "Does something.",
            method: POST,
            name: "my_endpoint",
            path: "/_matrix/foo/:bar/",
            rate_limited: false,
            requires_authentication: false,
        }

        request: {
            pub q2: Foo,

            pub bar: String,

            #[ruma_api(query)]
            pub baz: UserId,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }

        response: {
            pub q2: Vec<u8>,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }
    }
}

fn main() {}
<commit_msg>Move move-value test decl into logical mods for each<commit_after>// This tests that the "body" fields are moved after all other fields because they
// consume the request/response.

mod newtype_body {
    use ruma_api::ruma_api;
    use ruma_identifiers::UserId;

    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
    pub struct Foo;

    ruma_api! {
        metadata: {
            description: "Does something.",
            method: POST,
            name: "my_endpoint",
            path: "/_matrix/foo/:bar/",
            rate_limited: false,
            requires_authentication: false,
        }

        request: {
            #[ruma_api(body)]
            pub q2: Foo,

            #[ruma_api(path)]
            pub bar: String,

            #[ruma_api(query)]
            pub baz: UserId,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }

        response: {
            #[ruma_api(body)]
            pub q2: Foo,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }
    }
}

mod newtype_raw_body {
    use ruma_api::ruma_api;
    use ruma_identifiers::UserId;

    ruma_api! {
        metadata: {
            description: "Does something.",
            method: POST,
            name: "my_endpoint",
            path: "/_matrix/foo/:bar/",
            rate_limited: false,
            requires_authentication: false,
        }

        request: {
            #[ruma_api(raw_body)]
            pub q2: Vec<u8>,

            #[ruma_api(path)]
            pub bar: String,

            #[ruma_api(query)]
            pub baz: UserId,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }

        response: {
            #[ruma_api(raw_body)]
            pub q2: Vec<u8>,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }
    }
}

mod plain {
    use ruma_api::ruma_api;
    use ruma_identifiers::UserId;

    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
    pub struct Foo;

    ruma_api! {
        metadata: {
            description: "Does something.",
            method: POST,
            name: "my_endpoint",
            path: "/_matrix/foo/:bar/",
            rate_limited: false,
            requires_authentication: false,
        }

        request: {
            pub q2: Foo,

            pub bar: String,

            #[ruma_api(query)]
            pub baz: UserId,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }

        response: {
            pub q2: Vec<u8>,

            #[ruma_api(header = CONTENT_TYPE)]
            pub world: String,
        }
    }
}

fn main() {}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Modules/fs_fat/dir.rs
use kernel::prelude::*;
use kernel::lib::mem::aref::ArefBorrow;
use kernel::vfs::node;
use kernel::lib::byte_str::{ByteStr,ByteString};
use super::on_disk;
use super::file::FileNode;
use super::ClusterList;

pub type FilesystemInner = super::FilesystemInner;

pub struct DirNode
{
	fs: ArefBorrow<FilesystemInner>,
	start_cluster: u32,
	// - Uses the cluster chain
}

impl DirNode {
	pub fn new(fs: ArefBorrow<FilesystemInner>, start_cluster: u32) -> DirNode {
		DirNode {
			fs: fs,
			start_cluster: start_cluster,
		}
	}
	pub fn new_boxed(fs: ArefBorrow<FilesystemInner>, start_cluster: u32) -> Box<DirNode> {
		Box::new(Self::new(fs, start_cluster))
	}
}

impl node::NodeBase for DirNode {
	fn get_id(&self) -> node::InodeId {
		todo!("")
	}
}

impl DirNode {
	fn is_fixed_root(&self) -> bool {
		!is!(self.fs.ty, super::Size::Fat32) && self.start_cluster == self.fs.root_first_cluster
	}
	fn clusters(&self) -> ClusterList {
		if self.is_fixed_root() {
			let root_cluster_count = (self.fs.root_sector_count as usize + self.fs.spc-1) / self.fs.spc;
			ClusterList::Range(self.fs.root_first_cluster .. self.fs.root_first_cluster + root_cluster_count as u32)
		}
		else {
			ClusterList::Chained(self.fs.reborrow(), self.start_cluster)
		}
	}

	/// Locate a node in this directory by its first data cluster
	pub fn find_node(&self, ent_cluster: u32) -> Option<node::Node>
	{
		match self.find_ent_by_cluster(ent_cluster)
		{
		None => None,
		Some(e) =>
			if e.attributes & on_disk::ATTR_DIRECTORY != 0 {
				Some(node::Node::Dir(DirNode::new_boxed(self.fs.reborrow(), ent_cluster)))
			}
			else if e.attributes & on_disk::ATTR_VOLUMEID != 0 {
				None
			}
			else {
				Some(node::Node::File(FileNode::new_boxed(
					self.fs.reborrow(), self.start_cluster, ent_cluster, e.size
					)))
			},
		}
	}

	fn find_ent_by_cluster(&self, ent_cluster: u32) -> Option<DirEntShort> {
		for c in self.clusters()
		{
			let cluster = match self.fs.load_cluster(c) {
				Ok(v) => v,
				Err(_) => return None,
				};
			for ent in DirEnts::new(&cluster) {
				if let DirEnt::Short(e) = ent {
					if e.cluster == ent_cluster {
						return Some(e);
					}
				}
			}
		}
		None
	}
}

/// Iterator over directory entries
struct DirEnts<'a>
{
	cluster: &'a [u8],
	ofs: usize,
}

/// Directory entry returned by the DirEnts iterator
#[derive(Debug)]
enum DirEnt {
	End,
	Empty,
	Short(DirEntShort),
	Long(DirEntLong),
}
#[derive(Debug)]
struct DirEntShort {
	/// NUL-padded string with extention joined
	name: [u8; 11+1],
	cluster: u32,
	size: u32,
	attributes: u8,
	//creation_time: ::kernel::time::Timestamp,
	//modified_time: ::kernel::time::Timestamp,
	//accessed_time: ::kernel::time::Timestamp,
}
#[derive(Debug)]
struct DirEntLong {
	id: u8,
	_type: u8,
	chars: [u16; 13],
}
impl<'a> DirEnts<'a>
{
	fn new(data: &[u8]) -> DirEnts {
		assert_eq!(data.len() % 32, 0);
		DirEnts {
			cluster: data,
			ofs: 0,
		}
	}
}
impl<'a> ::core::iter::Iterator for DirEnts<'a> {
	type Item = DirEnt;
	fn next(&mut self) -> Option<DirEnt> {
		if self.ofs >= self.cluster.len() / 32 {
			None
		}
		else {
			let slice = &self.cluster[self.ofs*32..];
			self.ofs += 1;
			// Decode the legacy format entry
			let ent = on_disk::DirEnt::read(&mut &slice[..]);
			if ent.name[0] == 0 {
				Some(DirEnt::End)
			}
			else if ent.name[0] == b'\xE5' {
				Some(DirEnt::Empty)
			}
			else if ent.attribs == on_disk::ATTR_LFN {
				// Long filename entry
				let lfn = on_disk::DirEntLong::read(&mut &slice[..]);
				let outname = {
					let mut outname = [0u16; 13];
					outname[0..5].clone_from_slice(&lfn.name1);
					outname[5..11].clone_from_slice(&lfn.name2);
					outname[11..13].clone_from_slice(&lfn.name3);
					outname
					};
				Some(DirEnt::Long( DirEntLong{
					id: lfn.id,
					_type: lfn.ty,
					chars: outname,
					} ))
			}
			else {
				// Short entry
				// 1. Decode name into a NUL-padded string
				let (outname, _) = {
					let (mut outname, mut oidx) =  ([0u8; 8+1+3], 0);
					for iidx in (0 .. 8) {
						if ent.name[iidx] != b' ' {
							outname[oidx] = ent.name[iidx];
							oidx += 1;
						}
					}
					outname[oidx] = b'.';
					oidx += 1;
					for iidx in (8 .. 11) {
						if ent.name[iidx] != b' ' {
							outname[oidx] = ent.name[iidx];
							oidx += 1;
						}
					}
					if outname[oidx-1] == b'.' {
						outname[oidx-1] = 0;
						oidx -= 1;
					}
					(outname, oidx)
					};
				// 2. Cluster, Size, Attribs
				Some( DirEnt::Short(DirEntShort{
					name: outname,
					cluster: (ent.cluster as u32) | (ent.cluster_hi as u32) << 16,
					size: ent.size,
					attributes: ent.attribs,
					}) )
			}
		}
	}
}
impl DirEntShort {
	fn name(&self) -> &ByteStr {
		ByteStr::new( (&self.name).split(|&e|e==0).next().unwrap() )
	}
}

impl node::Dir for DirNode {
	fn lookup(&self, name: &ByteStr) -> node::Result<node::InodeId> {
		// For each cluster in the directory, iterate
		for c in self.clusters()
		{
			let cluster = try!(self.fs.load_cluster(c));
			for ent in DirEnts::new(&cluster) {
				log_debug!("ent = {:?}", ent);
				match ent {
				DirEnt::End => return Err(node::IoError::NotFound),
				DirEnt::Short(e) =>
					if e.name() == name {
						return Ok( super::InodeRef::new(e.cluster, self.start_cluster).to_id() );
					},
				DirEnt::Long(_) => log_log!("TODO: LFN"),
				DirEnt::Empty => {},
				}
			}
		}
		Err(node::IoError::NotFound)
	}
	fn read(&self, ofs: usize, items: &mut [(node::InodeId,ByteString)]) -> node::Result<(usize,usize)> {

		let ents_per_cluster = self.fs.cluster_size / 32;
		let cluster_idx = ofs / ents_per_cluster;
		let c_ofs = ofs % ents_per_cluster;
		let mut count = 0;
		// next_ofs is the return value, and is updated when a short entry is seen.
		let mut next_ofs = ofs+1;
		let mut cur_ofs = ofs;
		'outer: for c in self.clusters().skip(cluster_idx)
		{
			let cluster = try!(self.fs.load_cluster(c));
			for ent in DirEnts::new(&cluster).skip(c_ofs) {
				cur_ofs += 1;
				match ent {
				DirEnt::End => {
					// On next call, we want to hit this first shot
					next_ofs = cur_ofs-1;
					break 'outer;
					},
				DirEnt::Short(e) => {
					let i = super::InodeRef::new(e.cluster, self.start_cluster).to_id();
					items[count] = (i, ByteString::from(e.name()));
					count += 1;
					// Update return offset to be the next entry
					next_ofs = cur_ofs;
					if count == items.len() {
						break 'outer;
					}
					},
				DirEnt::Long(_) => log_log!("TODO: LFN"),
				DirEnt::Empty => {},
				}
			}
		}

		Ok( (next_ofs, count) )
	}
	fn create(&self, name: &ByteStr, nodetype: node::NodeType) -> node::Result<node::InodeId> {
		todo!("DirNode::create('{:?}', {:?})", name, nodetype);
	}
	fn link(&self, name: &ByteStr, inode: node::InodeId) -> node::Result<()> {
		todo!("DirNode::link('{:?}', {:#x})", name, inode);
	}
	fn unlink(&self, name: &ByteStr) -> node::Result<()> {
		todo!("DirNode::unlink('{:?}')", name);
	}
}

<commit_msg>Modules/FAT - Minor cleanup to readdir<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Modules/fs_fat/dir.rs
use kernel::prelude::*;
use kernel::lib::mem::aref::ArefBorrow;
use kernel::vfs::node;
use kernel::lib::byte_str::{ByteStr,ByteString};
use super::on_disk;
use super::file::FileNode;
use super::ClusterList;

pub type FilesystemInner = super::FilesystemInner;

pub struct DirNode
{
	fs: ArefBorrow<FilesystemInner>,
	start_cluster: u32,
	// - Uses the cluster chain
}

impl DirNode {
	pub fn new(fs: ArefBorrow<FilesystemInner>, start_cluster: u32) -> DirNode {
		DirNode {
			fs: fs,
			start_cluster: start_cluster,
		}
	}
	pub fn new_boxed(fs: ArefBorrow<FilesystemInner>, start_cluster: u32) -> Box<DirNode> {
		Box::new(Self::new(fs, start_cluster))
	}
}

impl node::NodeBase for DirNode {
	fn get_id(&self) -> node::InodeId {
		todo!("")
	}
}

impl DirNode {
	fn is_fixed_root(&self) -> bool {
		!is!(self.fs.ty, super::Size::Fat32) && self.start_cluster == self.fs.root_first_cluster
	}
	fn clusters(&self) -> ClusterList {
		if self.is_fixed_root() {
			let root_cluster_count = (self.fs.root_sector_count as usize + self.fs.spc-1) / self.fs.spc;
			ClusterList::Range(self.fs.root_first_cluster .. self.fs.root_first_cluster + root_cluster_count as u32)
		}
		else {
			ClusterList::Chained(self.fs.reborrow(), self.start_cluster)
		}
	}

	/// Locate a node in this directory by its first data cluster
	pub fn find_node(&self, ent_cluster: u32) -> Option<node::Node>
	{
		match self.find_ent_by_cluster(ent_cluster)
		{
		None => None,
		Some(e) =>
			if e.attributes & on_disk::ATTR_DIRECTORY != 0 {
				Some(node::Node::Dir(DirNode::new_boxed(self.fs.reborrow(), ent_cluster)))
			}
			else if e.attributes & on_disk::ATTR_VOLUMEID != 0 {
				None
			}
			else {
				Some(node::Node::File(FileNode::new_boxed(
					self.fs.reborrow(), self.start_cluster, ent_cluster, e.size
					)))
			},
		}
	}

	fn find_ent_by_cluster(&self, ent_cluster: u32) -> Option<DirEntShort> {
		for c in self.clusters()
		{
			let cluster = match self.fs.load_cluster(c) {
				Ok(v) => v,
				Err(_) => return None,
				};
			for ent in DirEnts::new(&cluster) {
				if let DirEnt::Short(e) = ent {
					if e.cluster == ent_cluster {
						return Some(e);
					}
				}
			}
		}
		None
	}
}

/// Iterator over directory entries
struct DirEnts<'a>
{
	cluster: &'a [u8],
	ofs: usize,
}

/// Directory entry returned by the DirEnts iterator
#[derive(Debug)]
enum DirEnt {
	End,
	Empty,
	Short(DirEntShort),
	Long(DirEntLong),
}
#[derive(Debug)]
struct DirEntShort {
	/// NUL-padded string with extention joined
	name: [u8; 11+1],
	cluster: u32,
	size: u32,
	attributes: u8,
	//creation_time: ::kernel::time::Timestamp,
	//modified_time: ::kernel::time::Timestamp,
	//accessed_time: ::kernel::time::Timestamp,
}
#[derive(Debug)]
struct DirEntLong {
	id: u8,
	_type: u8,
	chars: [u16; 13],
}
impl<'a> DirEnts<'a>
{
	fn new(data: &[u8]) -> DirEnts {
		assert_eq!(data.len() % 32, 0);
		DirEnts {
			cluster: data,
			ofs: 0,
		}
	}
}
impl<'a> ::core::iter::Iterator for DirEnts<'a> {
	type Item = DirEnt;
	fn next(&mut self) -> Option<DirEnt> {
		if self.ofs >= self.cluster.len() / 32 {
			None
		}
		else {
			let slice = &self.cluster[self.ofs*32..];
			self.ofs += 1;
			// Decode the legacy format entry
			let ent = on_disk::DirEnt::read(&mut &slice[..]);
			if ent.name[0] == 0 {
				Some(DirEnt::End)
			}
			else if ent.name[0] == b'\xE5' {
				Some(DirEnt::Empty)
			}
			else if ent.attribs == on_disk::ATTR_LFN {
				// Long filename entry
				let lfn = on_disk::DirEntLong::read(&mut &slice[..]);
				let outname = {
					let mut outname = [0u16; 13];
					outname[0..5].clone_from_slice(&lfn.name1);
					outname[5..11].clone_from_slice(&lfn.name2);
					outname[11..13].clone_from_slice(&lfn.name3);
					outname
					};
				Some(DirEnt::Long( DirEntLong{
					id: lfn.id,
					_type: lfn.ty,
					chars: outname,
					} ))
			}
			else {
				// Short entry
				// 1. Decode name into a NUL-padded string
				let (outname, _) = {
					let (mut outname, mut oidx) =  ([0u8; 8+1+3], 0);
					for iidx in (0 .. 8) {
						if ent.name[iidx] != b' ' {
							outname[oidx] = ent.name[iidx];
							oidx += 1;
						}
					}
					outname[oidx] = b'.';
					oidx += 1;
					for iidx in (8 .. 11) {
						if ent.name[iidx] != b' ' {
							outname[oidx] = ent.name[iidx];
							oidx += 1;
						}
					}
					if outname[oidx-1] == b'.' {
						outname[oidx-1] = 0;
						oidx -= 1;
					}
					(outname, oidx)
					};
				// 2. Cluster, Size, Attribs
				Some( DirEnt::Short(DirEntShort{
					name: outname,
					cluster: (ent.cluster as u32) | (ent.cluster_hi as u32) << 16,
					size: ent.size,
					attributes: ent.attribs,
					}) )
			}
		}
	}
}
impl DirEntShort {
	fn name(&self) -> &ByteStr {
		ByteStr::new( (&self.name).split(|&e|e==0).next().unwrap() )
	}
	fn inode(&self, parent_dir: u32) -> node::InodeId {
		super::InodeRef::new(self.cluster, parent_dir).to_id()
	}
}

impl node::Dir for DirNode {
	fn lookup(&self, name: &ByteStr) -> node::Result<node::InodeId> {
		// For each cluster in the directory, iterate
		for c in self.clusters()
		{
			let cluster = try!(self.fs.load_cluster(c));
			for ent in DirEnts::new(&cluster) {
				log_debug!("ent = {:?}", ent);
				match ent {
				DirEnt::End => return Err(node::IoError::NotFound),
				DirEnt::Short(e) =>
					if e.name() == name {
						return Ok( e.inode(self.start_cluster) );
					},
				DirEnt::Long(_) => log_log!("TODO: LFN"),
				DirEnt::Empty => {},
				}
			}
		}
		Err(node::IoError::NotFound)
	}
	fn read(&self, ofs: usize, items: &mut [(node::InodeId,ByteString)]) -> node::Result<(usize,usize)> {

		let ents_per_cluster = self.fs.cluster_size / 32;
		let (cluster_idx, c_ofs) = (ofs / ents_per_cluster, ofs % ents_per_cluster);

		let mut count = 0;
		let mut cur_ofs = ofs;
		'outer: for c in self.clusters().skip(cluster_idx)
		{
			let cluster = try!(self.fs.load_cluster(c));
			for ent in DirEnts::new(&cluster).skip(c_ofs) {
				cur_ofs += 1;
				match ent {
				DirEnt::End => {
					// On next call, we want to hit this entry (so we can return count=0)
					return Ok( (cur_ofs-1, count) );
					},
				DirEnt::Short(e) => {
					items[count] = (e.inode(self.start_cluster), ByteString::from(e.name()));
					count += 1;
					if count == items.len() {
						return Ok( (cur_ofs, count) );
					}
					},
				DirEnt::Long(_) => log_log!("TODO: LFN"),
				DirEnt::Empty => {},
				}
			}
		}

		Ok( (cur_ofs, count) )
	}
	fn create(&self, name: &ByteStr, nodetype: node::NodeType) -> node::Result<node::InodeId> {
		todo!("DirNode::create('{:?}', {:?})", name, nodetype);
	}
	fn link(&self, name: &ByteStr, inode: node::InodeId) -> node::Result<()> {
		todo!("DirNode::link('{:?}', {:#x})", name, inode);
	}
	fn unlink(&self, name: &ByteStr) -> node::Result<()> {
		todo!("DirNode::unlink('{:?}')", name);
	}
}

<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the /dev/bpf device.

use crate::bindings::bpf;
use crate::{DataLinkReceiver, DataLinkSender, NetworkInterface};

use pnet_sys;

use std::collections::VecDeque;
use std::ffi::CString;
use std::io;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::time::Duration;

static ETHERNET_HEADER_SIZE: usize = 14;

/// The BPF-specific configuration.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// The number of /dev/bpf* file descriptors to attempt before failing.
    ///
    /// This setting is only used on OS X - FreeBSD uses a single /dev/bpf rather than creating a
    /// new descriptor each time one is opened.
    ///
    /// Defaults to: 1000.
    pub bpf_fd_attempts: usize,
}

impl<'a> From<&'a super::Config> for Config {
    fn from(config: &super::Config) -> Config {
        Config {
            write_buffer_size: config.write_buffer_size,
            read_buffer_size: config.read_buffer_size,
            bpf_fd_attempts: config.bpf_fd_attempts,
            read_timeout: config.read_timeout,
            write_timeout: config.write_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            bpf_fd_attempts: 1000,
            read_timeout: None,
            write_timeout: None,
        }
    }
}

/// Create a datalink channel using the /dev/bpf device
// NOTE buffer must be word aligned.
#[inline]
pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
    fn get_fd(_attempts: usize) -> libc::c_int {
        unsafe {
            libc::open(
                CString::new(&b"/dev/bpf"[..]).unwrap().as_ptr(),
                libc::O_RDWR,
                0,
            )
        }
    }

    #[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios"))]
    fn get_fd(attempts: usize) -> libc::c_int {
        for i in 0..attempts {
            let fd = unsafe {
                let file_name = format!("/dev/bpf{}", i);
                libc::open(
                    CString::new(file_name.as_bytes()).unwrap().as_ptr(),
                    libc::O_RDWR,
                    0,
                )
            };
            if fd != -1 {
                return fd;
            }
        }

        -1
    }

    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
    fn set_feedback(fd: libc::c_int) -> io::Result<()> {
        if unsafe { bpf::ioctl(fd, bpf::BIOCFEEDBACK, &1) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
        Ok(())
    }

    #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "ios"))]
    fn set_feedback(_fd: libc::c_int) -> io::Result<()> {
        Ok(())
    }

    let fd = get_fd(config.bpf_fd_attempts);
    if fd == -1 {
        return Err(io::Error::last_os_error());
    }
    let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
    for (i, c) in network_interface.name.bytes().enumerate() {
        iface.ifr_name[i] = c as i8;
    }

    let buflen = config.read_buffer_size as libc::c_uint;
    // NOTE Buffer length must be set before binding to an interface
    //      otherwise this will return Invalid Argument
    if unsafe { bpf::ioctl(fd, bpf::BIOCSBLEN, &buflen) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Set the interface to use
    if unsafe { bpf::ioctl(fd, bpf::BIOCSETIF, &iface) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Return from read as soon as packets are available - don't wait to fill the
    // buffer
    if unsafe { bpf::ioctl(fd, bpf::BIOCIMMEDIATE, &1) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Get the device type
    let mut dlt: libc::c_uint = 0;
    if unsafe { bpf::ioctl(fd, bpf::BIOCGDLT, &mut dlt) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    let mut loopback = false;
    let mut allocated_read_buffer_size = config.read_buffer_size;
    // The loopback device does weird things
    // FIXME This should really just be another L2 packet header type
    if dlt == bpf::DLT_NULL {
        loopback = true;
        // So we can guaranatee that we can have a header before the packet.
        // Loopback packets arrive without the header.
        allocated_read_buffer_size += ETHERNET_HEADER_SIZE;

        // Allow packets to be read back after they are written
        if let Err(e) = set_feedback(fd) {
            return Err(e);
        }
    } else {
        // Don't fill in source MAC
        if unsafe { bpf::ioctl(fd, bpf::BIOCSHDRCMPLT, &1) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
    }

    // Enable nonblocking
    if unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            pnet_sys::close(fd);
        }
        return Err(err);
    }

    let fd = Arc::new(pnet_sys::FileDesc { fd: fd });
    let mut sender = Box::new(DataLinkSenderImpl {
        fd: fd.clone(),
        fd_set: unsafe { mem::zeroed() },
        write_buffer: vec![0; config.write_buffer_size],
        loopback: loopback,
        timeout: config
            .write_timeout
            .map(|to| pnet_sys::duration_to_timespec(to)),
    });
    unsafe {
        libc::FD_ZERO(&mut sender.fd_set as *mut libc::fd_set);
        libc::FD_SET(fd.fd, &mut sender.fd_set as *mut libc::fd_set);
    }
    let mut receiver = Box::new(DataLinkReceiverImpl {
        fd: fd.clone(),
        fd_set: unsafe { mem::zeroed() },
        read_buffer: vec![0; allocated_read_buffer_size],
        loopback: loopback,
        timeout: config
            .read_timeout
            .map(|to| pnet_sys::duration_to_timespec(to)),
        // Enough room for minimally sized packets without reallocating
        packets: VecDeque::with_capacity(allocated_read_buffer_size / 64),
    });
    unsafe {
        libc::FD_ZERO(&mut receiver.fd_set as *mut libc::fd_set);
        libc::FD_SET(fd.fd, &mut receiver.fd_set as *mut libc::fd_set);
    }

    Ok(super::Channel::Ethernet(sender, receiver))
}

struct DataLinkSenderImpl {
    fd: Arc<pnet_sys::FileDesc>,
    fd_set: libc::fd_set,
    write_buffer: Vec<u8>,
    loopback: bool,
    timeout: Option<libc::timespec>,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>> {
        let len = num_packets * packet_size;
        if len >= self.write_buffer.len() {
            None
        } else {
            // If we're sending on the loopback device, discard the ethernet header.
            // The OS will prepend the packet with 4 bytes set to AF_INET.
            let offset = if self.loopback {
                ETHERNET_HEADER_SIZE
            } else {
                0
            };
            for chunk in self.write_buffer[..len].chunks_mut(packet_size) {
                func(chunk);
                let ret = unsafe {
                    libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
                    libc::pselect(
                        self.fd.fd + 1,
                        ptr::null_mut(),
                        &mut self.fd_set as *mut libc::fd_set,
                        ptr::null_mut(),
                        self.timeout
                            .as_ref()
                            .map(|to| to as *const libc::timespec)
                            .unwrap_or(ptr::null()),
                        ptr::null(),
                    )
                };
                if ret == -1 {
                    // Error occured!
                    return Some(Err(io::Error::last_os_error()));
                } else if ret == 0 {
                    return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")));
                } else {
                    match unsafe {
                        libc::write(
                            self.fd.fd,
                            chunk.as_ptr().offset(offset as isize) as *const libc::c_void,
                            (chunk.len() - offset) as libc::size_t,
                        )
                    } {
                        len if len == -1 => return Some(Err(io::Error::last_os_error())),
                        _ => (),
                    }
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self, packet: &[u8], _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        // If we're sending on the loopback device, discard the ethernet header.
        // The OS will prepend the packet with 4 bytes set to AF_INET.
        let offset = if self.loopback {
            ETHERNET_HEADER_SIZE
        } else {
            0
        };
        let ret = unsafe {
            libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
            libc::pselect(
                self.fd.fd + 1,
                ptr::null_mut(),
                &mut self.fd_set as *mut libc::fd_set,
                ptr::null_mut(),
                self.timeout
                    .as_ref()
                    .map(|to| to as *const libc::timespec)
                    .unwrap_or(ptr::null()),
                ptr::null(),
            )
        };
        if ret == -1 {
            // Error occured!
            return Some(Err(io::Error::last_os_error()));
        } else if ret == 0 {
            return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")));
        } else {
            match unsafe {
                libc::write(
                    self.fd.fd,
                    packet.as_ptr().offset(offset as isize) as *const libc::c_void,
                    (packet.len() - offset) as libc::size_t,
                )
            } {
                len if len == -1 => Some(Err(io::Error::last_os_error())),
                _ => Some(Ok(())),
            }
        }
    }
}

struct DataLinkReceiverImpl {
    fd: Arc<pnet_sys::FileDesc>,
    fd_set: libc::fd_set,
    read_buffer: Vec<u8>,
    loopback: bool,
    timeout: Option<libc::timespec>,
    packets: VecDeque<(usize, usize)>,
}

impl DataLinkReceiver for DataLinkReceiverImpl {
    fn next(&mut self) -> io::Result<&[u8]> {
        // Loopback packets arrive with a 4 byte header instead of normal ethernet header.
        // Discard that header and replace with zeroed out ethernet header.
        let (header_size, buffer_offset) = if self.loopback {
            (4, ETHERNET_HEADER_SIZE)
        } else {
            (0, 0)
        };
        if self.packets.is_empty() {
            let buffer = &mut self.read_buffer[buffer_offset..];
            let ret = unsafe {
                libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
                libc::pselect(
                    self.fd.fd + 1,
                    &mut self.fd_set as *mut libc::fd_set,
                    ptr::null_mut(),
                    ptr::null_mut(),
                    self.timeout
                        .as_ref()
                        .map(|to| to as *const libc::timespec)
                        .unwrap_or(ptr::null()),
                    ptr::null(),
                )
            };
            if ret == -1 {
                return Err(io::Error::last_os_error());
            } else if ret == 0 {
                return Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"));
            } else {
                let buflen = match unsafe {
                    libc::read(
                        self.fd.fd,
                        buffer.as_ptr() as *mut libc::c_void,
                        buffer.len() as libc::size_t,
                    )
                } {
                    len if len > 0 => len,
                    _ => return Err(io::Error::last_os_error()),
                };
                let mut ptr = buffer.as_mut_ptr();
                let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
                while (ptr as *const u8) < end {
                    unsafe {
                        let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                        let start =
                            ptr as isize + (*packet).bh_hdrlen as isize - buffer.as_ptr() as isize;
                        self.packets.push_back((
                            start as usize + header_size,
                            (*packet).bh_caplen as usize - header_size,
                        ));
                        let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                        ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                    }
                }
            }
        }
        let (start, mut len) = self.packets.pop_front().unwrap();
        len += buffer_offset;
        // Zero out part that will become fake ethernet header if on loopback.
        for i in (&mut self.read_buffer[start..start + buffer_offset]).iter_mut() {
            *i = 0;
        }
        Ok(&self.read_buffer[start..start + len])
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    #[path = "unix_interfaces.rs"]
    mod interfaces;
    interfaces::interfaces()
}
<commit_msg>Fix build lifetime issue on macOS<commit_after>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the /dev/bpf device.

use crate::bindings::bpf;
use crate::{DataLinkReceiver, DataLinkSender, NetworkInterface};

use pnet_sys;

use std::collections::VecDeque;
use std::ffi::CString;
use std::io;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::time::Duration;

static ETHERNET_HEADER_SIZE: usize = 14;

/// The BPF-specific configuration.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// The number of /dev/bpf* file descriptors to attempt before failing.
    ///
    /// This setting is only used on OS X - FreeBSD uses a single /dev/bpf rather than creating a
    /// new descriptor each time one is opened.
    ///
    /// Defaults to: 1000.
    pub bpf_fd_attempts: usize,
}

impl<'a> From<&'a super::Config> for Config {
    fn from(config: &super::Config) -> Config {
        Config {
            write_buffer_size: config.write_buffer_size,
            read_buffer_size: config.read_buffer_size,
            bpf_fd_attempts: config.bpf_fd_attempts,
            read_timeout: config.read_timeout,
            write_timeout: config.write_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            bpf_fd_attempts: 1000,
            read_timeout: None,
            write_timeout: None,
        }
    }
}

/// Create a datalink channel using the /dev/bpf device
// NOTE buffer must be word aligned.
#[inline]
pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
    fn get_fd(_attempts: usize) -> libc::c_int {
        unsafe {
            libc::open(
                CString::new(&b"/dev/bpf"[..]).unwrap().as_ptr(),
                libc::O_RDWR,
                0,
            )
        }
    }

    #[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios"))]
    fn get_fd(attempts: usize) -> libc::c_int {
        for i in 0..attempts {
            let fd = unsafe {
                let file_name = format!("/dev/bpf{}", i);
                let c_file_name = CString::new(file_name.as_bytes()).unwrap();
                libc::open(
                    c_file_name.as_ptr(),
                    libc::O_RDWR,
                    0,
                )
            };
            if fd != -1 {
                return fd;
            }
        }

        -1
    }

    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
    fn set_feedback(fd: libc::c_int) -> io::Result<()> {
        if unsafe { bpf::ioctl(fd, bpf::BIOCFEEDBACK, &1) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
        Ok(())
    }

    #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "ios"))]
    fn set_feedback(_fd: libc::c_int) -> io::Result<()> {
        Ok(())
    }

    let fd = get_fd(config.bpf_fd_attempts);
    if fd == -1 {
        return Err(io::Error::last_os_error());
    }
    let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
    for (i, c) in network_interface.name.bytes().enumerate() {
        iface.ifr_name[i] = c as i8;
    }

    let buflen = config.read_buffer_size as libc::c_uint;
    // NOTE Buffer length must be set before binding to an interface
    //      otherwise this will return Invalid Argument
    if unsafe { bpf::ioctl(fd, bpf::BIOCSBLEN, &buflen) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Set the interface to use
    if unsafe { bpf::ioctl(fd, bpf::BIOCSETIF, &iface) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Return from read as soon as packets are available - don't wait to fill the
    // buffer
    if unsafe { bpf::ioctl(fd, bpf::BIOCIMMEDIATE, &1) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    // Get the device type
    let mut dlt: libc::c_uint = 0;
    if unsafe { bpf::ioctl(fd, bpf::BIOCGDLT, &mut dlt) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            libc::close(fd);
        }
        return Err(err);
    }

    let mut loopback = false;
    let mut allocated_read_buffer_size = config.read_buffer_size;
    // The loopback device does weird things
    // FIXME This should really just be another L2 packet header type
    if dlt == bpf::DLT_NULL {
        loopback = true;
        // So we can guaranatee that we can have a header before the packet.
        // Loopback packets arrive without the header.
        allocated_read_buffer_size += ETHERNET_HEADER_SIZE;

        // Allow packets to be read back after they are written
        if let Err(e) = set_feedback(fd) {
            return Err(e);
        }
    } else {
        // Don't fill in source MAC
        if unsafe { bpf::ioctl(fd, bpf::BIOCSHDRCMPLT, &1) } == -1 {
            let err = io::Error::last_os_error();
            unsafe {
                libc::close(fd);
            }
            return Err(err);
        }
    }

    // Enable nonblocking
    if unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) } == -1 {
        let err = io::Error::last_os_error();
        unsafe {
            pnet_sys::close(fd);
        }
        return Err(err);
    }

    let fd = Arc::new(pnet_sys::FileDesc { fd: fd });
    let mut sender = Box::new(DataLinkSenderImpl {
        fd: fd.clone(),
        fd_set: unsafe { mem::zeroed() },
        write_buffer: vec![0; config.write_buffer_size],
        loopback: loopback,
        timeout: config
            .write_timeout
            .map(|to| pnet_sys::duration_to_timespec(to)),
    });
    unsafe {
        libc::FD_ZERO(&mut sender.fd_set as *mut libc::fd_set);
        libc::FD_SET(fd.fd, &mut sender.fd_set as *mut libc::fd_set);
    }
    let mut receiver = Box::new(DataLinkReceiverImpl {
        fd: fd.clone(),
        fd_set: unsafe { mem::zeroed() },
        read_buffer: vec![0; allocated_read_buffer_size],
        loopback: loopback,
        timeout: config
            .read_timeout
            .map(|to| pnet_sys::duration_to_timespec(to)),
        // Enough room for minimally sized packets without reallocating
        packets: VecDeque::with_capacity(allocated_read_buffer_size / 64),
    });
    unsafe {
        libc::FD_ZERO(&mut receiver.fd_set as *mut libc::fd_set);
        libc::FD_SET(fd.fd, &mut receiver.fd_set as *mut libc::fd_set);
    }

    Ok(super::Channel::Ethernet(sender, receiver))
}

struct DataLinkSenderImpl {
    fd: Arc<pnet_sys::FileDesc>,
    fd_set: libc::fd_set,
    write_buffer: Vec<u8>,
    loopback: bool,
    timeout: Option<libc::timespec>,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>> {
        let len = num_packets * packet_size;
        if len >= self.write_buffer.len() {
            None
        } else {
            // If we're sending on the loopback device, discard the ethernet header.
            // The OS will prepend the packet with 4 bytes set to AF_INET.
            let offset = if self.loopback {
                ETHERNET_HEADER_SIZE
            } else {
                0
            };
            for chunk in self.write_buffer[..len].chunks_mut(packet_size) {
                func(chunk);
                let ret = unsafe {
                    libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
                    libc::pselect(
                        self.fd.fd + 1,
                        ptr::null_mut(),
                        &mut self.fd_set as *mut libc::fd_set,
                        ptr::null_mut(),
                        self.timeout
                            .as_ref()
                            .map(|to| to as *const libc::timespec)
                            .unwrap_or(ptr::null()),
                        ptr::null(),
                    )
                };
                if ret == -1 {
                    // Error occured!
                    return Some(Err(io::Error::last_os_error()));
                } else if ret == 0 {
                    return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")));
                } else {
                    match unsafe {
                        libc::write(
                            self.fd.fd,
                            chunk.as_ptr().offset(offset as isize) as *const libc::c_void,
                            (chunk.len() - offset) as libc::size_t,
                        )
                    } {
                        len if len == -1 => return Some(Err(io::Error::last_os_error())),
                        _ => (),
                    }
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self, packet: &[u8], _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        // If we're sending on the loopback device, discard the ethernet header.
        // The OS will prepend the packet with 4 bytes set to AF_INET.
        let offset = if self.loopback {
            ETHERNET_HEADER_SIZE
        } else {
            0
        };
        let ret = unsafe {
            libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
            libc::pselect(
                self.fd.fd + 1,
                ptr::null_mut(),
                &mut self.fd_set as *mut libc::fd_set,
                ptr::null_mut(),
                self.timeout
                    .as_ref()
                    .map(|to| to as *const libc::timespec)
                    .unwrap_or(ptr::null()),
                ptr::null(),
            )
        };
        if ret == -1 {
            // Error occured!
            return Some(Err(io::Error::last_os_error()));
        } else if ret == 0 {
            return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")));
        } else {
            match unsafe {
                libc::write(
                    self.fd.fd,
                    packet.as_ptr().offset(offset as isize) as *const libc::c_void,
                    (packet.len() - offset) as libc::size_t,
                )
            } {
                len if len == -1 => Some(Err(io::Error::last_os_error())),
                _ => Some(Ok(())),
            }
        }
    }
}

struct DataLinkReceiverImpl {
    fd: Arc<pnet_sys::FileDesc>,
    fd_set: libc::fd_set,
    read_buffer: Vec<u8>,
    loopback: bool,
    timeout: Option<libc::timespec>,
    packets: VecDeque<(usize, usize)>,
}

impl DataLinkReceiver for DataLinkReceiverImpl {
    fn next(&mut self) -> io::Result<&[u8]> {
        // Loopback packets arrive with a 4 byte header instead of normal ethernet header.
        // Discard that header and replace with zeroed out ethernet header.
        let (header_size, buffer_offset) = if self.loopback {
            (4, ETHERNET_HEADER_SIZE)
        } else {
            (0, 0)
        };
        if self.packets.is_empty() {
            let buffer = &mut self.read_buffer[buffer_offset..];
            let ret = unsafe {
                libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set);
                libc::pselect(
                    self.fd.fd + 1,
                    &mut self.fd_set as *mut libc::fd_set,
                    ptr::null_mut(),
                    ptr::null_mut(),
                    self.timeout
                        .as_ref()
                        .map(|to| to as *const libc::timespec)
                        .unwrap_or(ptr::null()),
                    ptr::null(),
                )
            };
            if ret == -1 {
                return Err(io::Error::last_os_error());
            } else if ret == 0 {
                return Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"));
            } else {
                let buflen = match unsafe {
                    libc::read(
                        self.fd.fd,
                        buffer.as_ptr() as *mut libc::c_void,
                        buffer.len() as libc::size_t,
                    )
                } {
                    len if len > 0 => len,
                    _ => return Err(io::Error::last_os_error()),
                };
                let mut ptr = buffer.as_mut_ptr();
                let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
                while (ptr as *const u8) < end {
                    unsafe {
                        let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                        let start =
                            ptr as isize + (*packet).bh_hdrlen as isize - buffer.as_ptr() as isize;
                        self.packets.push_back((
                            start as usize + header_size,
                            (*packet).bh_caplen as usize - header_size,
                        ));
                        let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                        ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                    }
                }
            }
        }
        let (start, mut len) = self.packets.pop_front().unwrap();
        len += buffer_offset;
        // Zero out part that will become fake ethernet header if on loopback.
        for i in (&mut self.read_buffer[start..start + buffer_offset]).iter_mut() {
            *i = 0;
        }
        Ok(&self.read_buffer[start..start + len])
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    #[path = "unix_interfaces.rs"]
    mod interfaces;
    interfaces::interfaces()
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use std::{borrow::Cow, path::Path};

static LICENSE_HEADER: &str = "Copyright (c) The Libra Core Contributors\n\
                               SPDX-License-Identifier: Apache-2.0\n\
                               ";

pub(super) fn has_license_header(file: &Path, contents: &str) -> Result<(), Cow<'static, str>> {
    enum FileType {
        Rust,
        Shell,
        Proto,
    }

    let file_type = if let Some(extension) = file.extension() {
        if extension == "rs" {
            FileType::Rust
        } else if extension == "sh" {
            FileType::Shell
        } else if extension == "proto" {
            FileType::Proto
        } else {
            return Ok(());
        }
    } else {
        return Ok(());
    };

    // Determine if the file is missing the license header
    let missing_header = match file_type {
        FileType::Rust | FileType::Proto => {
            let maybe_license = contents
                .lines()
                .skip_while(|line| line.is_empty())
                .take(2)
                .map(|s| s.trim_start_matches("// "));
            !LICENSE_HEADER.lines().eq(maybe_license)
        }
        FileType::Shell => {
            let maybe_license = contents
                .lines()
                .skip_while(|line| line.starts_with("#!"))
                .skip_while(|line| line.is_empty())
                .take(2)
                .map(|s| s.trim_start_matches("# "));
            !LICENSE_HEADER.lines().eq(maybe_license)
        }
    };

    if missing_header {
        return Err("missing a license header".into());
    }

    Ok(())
}
<commit_msg>[x] simplify extension checking in license lint<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use std::{borrow::Cow, convert, ffi::OsStr, path::Path};

static LICENSE_HEADER: &str = "Copyright (c) The Libra Core Contributors\n\
                               SPDX-License-Identifier: Apache-2.0\n\
                               ";

pub(super) fn has_license_header(file: &Path, contents: &str) -> Result<(), Cow<'static, str>> {
    enum FileType {
        Rust,
        Shell,
        Proto,
    }

    let file_type = match file
        .extension()
        .map(OsStr::to_str)
        .and_then(convert::identity)
    {
        Some("rs") => FileType::Rust,
        Some("sh") => FileType::Shell,
        Some("proto") => FileType::Proto,
        _ => return Ok(()),
    };

    // Determine if the file is missing the license header
    let missing_header = match file_type {
        FileType::Rust | FileType::Proto => {
            let maybe_license = contents
                .lines()
                .skip_while(|line| line.is_empty())
                .take(2)
                .map(|s| s.trim_start_matches("// "));
            !LICENSE_HEADER.lines().eq(maybe_license)
        }
        FileType::Shell => {
            let maybe_license = contents
                .lines()
                .skip_while(|line| line.starts_with("#!"))
                .skip_while(|line| line.is_empty())
                .take(2)
                .map(|s| s.trim_start_matches("# "));
            !LICENSE_HEADER.lines().eq(maybe_license)
        }
    };

    if missing_header {
        return Err("missing a license header".into());
    }

    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// TODO: other background-* properties
<%helpers:shorthand name="background"
                    sub_properties="background-color background-position-x background-position-y background-repeat
                                    background-attachment background-image background-size background-origin
                                    background-clip"
                    spec="https://drafts.csswg.org/css-backgrounds/#the-background">
    use properties::longhands::{background_position_x, background_position_y, background_repeat};
    use properties::longhands::{background_attachment, background_image, background_size, background_origin};
    use properties::longhands::background_clip;
    use properties::longhands::background_clip::single_value::computed_value::T as Clip;
    use properties::longhands::background_origin::single_value::computed_value::T as Origin;
    use values::specified::{CSSColor, Position, PositionComponent};
    use parser::Parse;

    impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue {
        fn from(origin: background_origin::single_value::SpecifiedValue) ->
            background_clip::single_value::SpecifiedValue {
            match origin {
                background_origin::single_value::SpecifiedValue::content_box =>
                    background_clip::single_value::SpecifiedValue::content_box,
                background_origin::single_value::SpecifiedValue::padding_box =>
                    background_clip::single_value::SpecifiedValue::padding_box,
                background_origin::single_value::SpecifiedValue::border_box =>
                    background_clip::single_value::SpecifiedValue::border_box,
            }
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut background_color = None;

        % for name in "image position_x position_y repeat size attachment origin clip".split():
            let mut background_${name} = background_${name}::SpecifiedValue(Vec::new());
        % endfor
        try!(input.parse_comma_separated(|input| {
            % for name in "image position repeat size attachment origin clip".split():
                let mut ${name} = None;
            % endfor
            loop {
                if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) {
                    if background_color.is_none() {
                        background_color = Some(value);
                        continue
                    } else {
                        // color can only be the last element
                        return Err(())
                    }
                }
                if position.is_none() {
                    if let Ok(value) = input.try(|input| Position::parse(context, input)) {
                        position = Some(value);

                        // Parse background size, if applicable.
                        size = input.try(|input| {
                            try!(input.expect_delim('/'));
                            background_size::single_value::parse(context, input)
                        }).ok();

                        continue
                    }
                }
                % for name in "image repeat attachment origin clip".split():
                    if ${name}.is_none() {
                        if let Ok(value) = input.try(|input| background_${name}::single_value
                                                                               ::parse(context, input)) {
                            ${name} = Some(value);
                            continue
                        }
                    }
                % endfor
                break
            }
            if clip.is_none() {
                if let Some(origin) = origin {
                    clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
                }
            }
            let mut any = false;
            % for name in "image position repeat size attachment origin clip".split():
                any = any || ${name}.is_some();
            % endfor
            any = any || background_color.is_some();
            if any {
                if let Some(position) = position {
                    background_position_x.0.push(position.horizontal);
                    background_position_y.0.push(position.vertical);
                } else {
                    background_position_x.0.push(PositionComponent::zero());
                    background_position_y.0.push(PositionComponent::zero());
                }
                % for name in "image repeat size attachment origin clip".split():
                    if let Some(bg_${name}) = ${name} {
                        background_${name}.0.push(bg_${name});
                    } else {
                        background_${name}.0.push(background_${name}::single_value
                                                                    ::get_initial_specified_value());
                    }
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }));

        Ok(expanded! {
             background_color: background_color.unwrap_or(CSSColor::transparent()),
             background_image: background_image,
             background_position_x: background_position_x,
             background_position_y: background_position_y,
             background_repeat: background_repeat,
             background_attachment: background_attachment,
             background_size: background_size,
             background_origin: background_origin,
             background_clip: background_clip,
         })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.background_image.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If a value list length is differs then we don't do a shorthand serialization.
            // The exceptions to this is color which appears once only and is serialized
            // with the last item.
            % for name in "image position_x position_y size repeat origin clip attachment".split():
                if len != self.background_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                % for name in "image position_x position_y repeat size attachment origin clip".split():
                    let ${name} = &self.background_${name}.0[i];
                % endfor

                if i != 0 {
                    try!(write!(dest, ", "));
                }

                if i == len - 1 {
                    try!(self.background_color.to_css(dest));
                    try!(write!(dest, " "));
                }

                try!(image.to_css(dest));
                % for name in "repeat attachment".split():
                    try!(write!(dest, " "));
                    try!(${name}.to_css(dest));
                % endfor

                try!(write!(dest, " "));
                Position {
                    horizontal: position_x.clone(),
                    vertical: position_y.clone()
                }.to_css(dest)?;

                if *size != background_size::single_value::get_initial_specified_value() {
                    try!(write!(dest, " / "));
                    try!(size.to_css(dest));
                }

                if *origin != Origin::padding_box || *clip != Clip::border_box {
                    try!(write!(dest, " "));
                    try!(origin.to_css(dest));
                    if *clip != From::from(*origin) {
                        try!(write!(dest, " "));
                        try!(clip.to_css(dest));
                    }
                }
            }

            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="background-position"
                    sub_properties="background-position-x background-position-y"
                    spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
    use properties::longhands::{background_position_x, background_position_y};
    use values::specified::AllowQuirks;
    use values::specified::position::Position;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut position_x = background_position_x::SpecifiedValue(Vec::new());
        let mut position_y = background_position_y::SpecifiedValue(Vec::new());
        let mut any = false;

        input.parse_comma_separated(|input| {
            let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?;
            position_x.0.push(value.horizontal);
            position_y.0.push(value.vertical);
            any = true;
            Ok(())
        })?;
        if !any {
            return Err(());
        }

        Ok(expanded! {
            background_position_x: position_x,
            background_position_y: position_y,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.background_position_x.0.len();
            if len == 0 || len != self.background_position_y.0.len() {
                return Ok(());
            }
            for i in 0..len {
                Position {
                    horizontal: self.background_position_x.0[i].clone(),
                    vertical: self.background_position_y.0[i].clone()
                }.to_css(dest)?;

                if i < len - 1 {
                    dest.write_str(", ")?;
                }
            }
            Ok(())
        }
    }
</%helpers:shorthand>
<commit_msg>Reject "text" value in background shorthand.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// TODO: other background-* properties
<%helpers:shorthand name="background"
                    sub_properties="background-color background-position-x background-position-y background-repeat
                                    background-attachment background-image background-size background-origin
                                    background-clip"
                    spec="https://drafts.csswg.org/css-backgrounds/#the-background">
    use properties::longhands::{background_position_x, background_position_y, background_repeat};
    use properties::longhands::{background_attachment, background_image, background_size, background_origin};
    use properties::longhands::background_clip;
    use properties::longhands::background_clip::single_value::computed_value::T as Clip;
    use properties::longhands::background_origin::single_value::computed_value::T as Origin;
    use values::specified::{CSSColor, Position, PositionComponent};
    use parser::Parse;

    impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue {
        fn from(origin: background_origin::single_value::SpecifiedValue) ->
            background_clip::single_value::SpecifiedValue {
            match origin {
                background_origin::single_value::SpecifiedValue::content_box =>
                    background_clip::single_value::SpecifiedValue::content_box,
                background_origin::single_value::SpecifiedValue::padding_box =>
                    background_clip::single_value::SpecifiedValue::padding_box,
                background_origin::single_value::SpecifiedValue::border_box =>
                    background_clip::single_value::SpecifiedValue::border_box,
            }
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut background_color = None;

        % for name in "image position_x position_y repeat size attachment origin clip".split():
            let mut background_${name} = background_${name}::SpecifiedValue(Vec::new());
        % endfor
        try!(input.parse_comma_separated(|input| {
            % for name in "image position repeat size attachment origin clip".split():
                let mut ${name} = None;
            % endfor
            loop {
                if let Ok(value) = input.try(|i| CSSColor::parse(context, i)) {
                    if background_color.is_none() {
                        background_color = Some(value);
                        continue
                    } else {
                        // color can only be the last element
                        return Err(())
                    }
                }
                if position.is_none() {
                    if let Ok(value) = input.try(|input| Position::parse(context, input)) {
                        position = Some(value);

                        // Parse background size, if applicable.
                        size = input.try(|input| {
                            try!(input.expect_delim('/'));
                            background_size::single_value::parse(context, input)
                        }).ok();

                        continue
                    }
                }
                % for name in "image repeat attachment origin clip".split():
                    if ${name}.is_none() {
                        if let Ok(value) = input.try(|input| background_${name}::single_value
                                                                               ::parse(context, input)) {
                            % if name == "clip" and product == "gecko":
                            // "text" value of background-clip should not be part of background
                            // shorthand per current spec and impls.
                            if value == background_clip::single_value::SpecifiedValue::text {
                                return Err(());
                            }
                            % endif
                            ${name} = Some(value);
                            continue
                        }
                    }
                % endfor
                break
            }
            if clip.is_none() {
                if let Some(origin) = origin {
                    clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
                }
            }
            let mut any = false;
            % for name in "image position repeat size attachment origin clip".split():
                any = any || ${name}.is_some();
            % endfor
            any = any || background_color.is_some();
            if any {
                if let Some(position) = position {
                    background_position_x.0.push(position.horizontal);
                    background_position_y.0.push(position.vertical);
                } else {
                    background_position_x.0.push(PositionComponent::zero());
                    background_position_y.0.push(PositionComponent::zero());
                }
                % for name in "image repeat size attachment origin clip".split():
                    if let Some(bg_${name}) = ${name} {
                        background_${name}.0.push(bg_${name});
                    } else {
                        background_${name}.0.push(background_${name}::single_value
                                                                    ::get_initial_specified_value());
                    }
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }));

        Ok(expanded! {
             background_color: background_color.unwrap_or(CSSColor::transparent()),
             background_image: background_image,
             background_position_x: background_position_x,
             background_position_y: background_position_y,
             background_repeat: background_repeat,
             background_attachment: background_attachment,
             background_size: background_size,
             background_origin: background_origin,
             background_clip: background_clip,
         })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.background_image.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If a value list length is differs then we don't do a shorthand serialization.
            // The exceptions to this is color which appears once only and is serialized
            // with the last item.
            % for name in "image position_x position_y size repeat origin clip attachment".split():
                if len != self.background_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                % for name in "image position_x position_y repeat size attachment origin clip".split():
                    let ${name} = &self.background_${name}.0[i];
                % endfor

                if i != 0 {
                    try!(write!(dest, ", "));
                }

                if i == len - 1 {
                    try!(self.background_color.to_css(dest));
                    try!(write!(dest, " "));
                }

                try!(image.to_css(dest));
                % for name in "repeat attachment".split():
                    try!(write!(dest, " "));
                    try!(${name}.to_css(dest));
                % endfor

                try!(write!(dest, " "));
                Position {
                    horizontal: position_x.clone(),
                    vertical: position_y.clone()
                }.to_css(dest)?;

                if *size != background_size::single_value::get_initial_specified_value() {
                    try!(write!(dest, " / "));
                    try!(size.to_css(dest));
                }

                if *origin != Origin::padding_box || *clip != Clip::border_box {
                    try!(write!(dest, " "));
                    try!(origin.to_css(dest));
                    if *clip != From::from(*origin) {
                        try!(write!(dest, " "));
                        try!(clip.to_css(dest));
                    }
                }
            }

            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="background-position"
                    sub_properties="background-position-x background-position-y"
                    spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
    use properties::longhands::{background_position_x, background_position_y};
    use values::specified::AllowQuirks;
    use values::specified::position::Position;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut position_x = background_position_x::SpecifiedValue(Vec::new());
        let mut position_y = background_position_y::SpecifiedValue(Vec::new());
        let mut any = false;

        input.parse_comma_separated(|input| {
            let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?;
            position_x.0.push(value.horizontal);
            position_y.0.push(value.vertical);
            any = true;
            Ok(())
        })?;
        if !any {
            return Err(());
        }

        Ok(expanded! {
            background_position_x: position_x,
            background_position_y: position_y,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.background_position_x.0.len();
            if len == 0 || len != self.background_position_y.0.len() {
                return Ok(());
            }
            for i in 0..len {
                Position {
                    horizontal: self.background_position_x.0[i].clone(),
                    vertical: self.background_position_y.0[i].clone()
                }.to_css(dest)?;

                if i < len - 1 {
                    dest.write_str(", ")?;
                }
            }
            Ok(())
        }
    }
</%helpers:shorthand>
<|endoftext|>