<commit_before>
use std::result;
use futures;
use futures::Async;
use tokio_core::io::EasyBuf;

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Error;

pub type Result<T> = result::Result<T, Error>;
pub type Poll<T> = futures::Poll<T, Error>;

//------------ Combinator-y Things -------------------------------------------

pub fn try<F, T>(buf: &mut EasyBuf, op: F) -> Poll<T>
           where F: FnOnce(&mut EasyBuf) -> Poll<T> {
    let orig_buf = buf.clone();
    let res = op(buf);
    match res {
        Ok(Async::NotReady) | Err(_) => *buf = orig_buf,
        _ => {}
    }
    res
}

//------------ Various Parsing -----------------------------------------------

pub fn octet(buf: &mut EasyBuf, octet: u8) -> Poll<()> {
    if buf.len() == 0 {
        Ok(Async::NotReady)
    }
    else if buf.as_slice()[0] == octet {
        buf.drain_to(1);
        Ok(Async::Ready(()))
    }
    else {
        Err(Error)
    }
}

/// Parses a literal sequence of octets.
///
/// If the start of `buf` matches `literal` case-insensitively, drains the
/// length of `literal` from the buffer and ready-returns.  If `buf` starts
/// with an incomplete match, returns non-ready. Otherwise returns an error.
pub fn literal(buf: &mut EasyBuf, literal: &[u8]) -> Poll<()> {
    use std::cmp::min;
    use std::ascii::AsciiExt;

    let litlen = {
        let len = buf.len();
        let litlen = literal.len();
        let minlen = min(len, litlen);
        let reduced = &buf.as_slice()[..minlen];
        let litreduced = &literal[..minlen];

        if !reduced.eq_ignore_ascii_case(litreduced) {
            return Err(Error)
        }
        else if minlen < litlen {
            return Ok(Async::NotReady)
        }
        litlen
    };
    let _ = buf.drain_to(litlen);
    Ok(Async::Ready(()))
}

//------------ Category Octet Parsing ----------------------------------------

/// Parses a single octet that matches a function.
///
/// If `test` returns `true` for first octet in `buf`, drains that octet from
/// `buf` and ready-returns it. If `buf` is empty returns non-ready. If there
/// is a first octet but `test` returns `false`, returns an error.
pub fn cat<F>(buf: &mut EasyBuf, test: F) -> Poll<u8>
           where F: FnOnce(u8) -> bool {
    if buf.len() < 1 {
        Ok(Async::NotReady)
    }
    else {
        let ch = buf.as_slice()[0];
        if test(ch) {
            let _ = buf.drain_to(1);
            Ok(Async::Ready(ch))
        }
        else {
            Err(Error)
        }
    }
}

/// Parses a non-empty sequence of octets matched by a function.
///
/// If `buf` starts with a sequence of at least one octet for which `test`
/// returns `true` which is followed by an octet for which `test` returns
/// `false`, drains the sequence from `buf` and ready-returns it. If there
/// is a sequence but no following octet, returns not ready. If the first
/// octet in `buf` is not matched by `test`, returns an error.
///
/// This function is used for parsing sequences in the middle of a message.
/// If the message can ends with the sequence, use `final_cats()` instead.
pub fn cats<F>(buf: &mut EasyBuf, test: F) -> Poll<EasyBuf>
            where F: Fn(u8) -> bool {
    if buf.len() < 1 {
        return Ok(Async::NotReady)
    }
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(0) => Err(Error),
        Some(end) => Ok(Async::Ready(buf.drain_to(end)))
    }
}

/// Parses a final non-empty sequence of octets matched by a function.
///
/// If `buf` consists entirly of a sequence of octets matched by `test`,
/// returns a clone of `buf`. Otherwise returns an error.
pub fn final_cats<F>(buf: &mut EasyBuf, test: F) -> Result<EasyBuf>
                  where F: Fn(u8) -> bool {
    let len = buf.len();
    if len == 0 {
        return Err(Error)
    }
    for item in buf.as_slice().iter() {
        if !test(*item) {
            return Err(Error);
        }
    }
    Ok(buf.drain_to(len))
}

/// Parses an optional sequence of octets matched by a function.
pub fn opt_cats<F>(buf: &mut EasyBuf, test: F) -> Poll<Option<EasyBuf>>
                where F: Fn(u8) -> bool {
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(0) => Ok(Async::Ready(None)),
        Some(end) => Ok(Async::Ready(Some(buf.drain_to(end))))
    }
}

/// Parses an option of at least `n` and at most `m` octets matched by `test`.
///
/// # Panics
///
/// The function panics if `m` is not greater than `n`.
pub fn nm_cats<F>(buf: &mut EasyBuf, n: usize, m: usize, test: F)
                  -> Poll<EasyBuf>
               where F: Fn(u8) -> bool {
    assert!(m > n);
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(end) if end < n || end >= m => Err(Error),
        Some(end) => Ok(Async::Ready(buf.drain_to(end)))
    }
}

pub fn cats_cat<F>(buf: &mut EasyBuf, word: F, delim: F) -> Poll<EasyBuf>
                where F: Fn(u8) -> bool {
    if buf.len() < 2 {
        return Ok(Async::NotReady)
    }
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if delim(*item) {
            end = Some(index);
            break;
        }
        else if !word(*item) {
            return Err(Error)
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(end) => {
            let res = buf.drain_to(end);
            buf.drain_to(1);
            Ok(Async::Ready(res))
        }
    }
}

<commit_msg>New repeat() and repeat_len() functions.<commit_after>
use std::result;
use futures;
use futures::Async;
use tokio_core::io::EasyBuf;

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Error;

pub type Result<T> = result::Result<T, Error>;
pub type Poll<T> = futures::Poll<T, Error>;

//------------ Combinator-y Things -------------------------------------------

pub fn try<F, T>(buf: &mut EasyBuf, op: F) -> Poll<T>
           where F: FnOnce(&mut EasyBuf) -> Poll<T> {
    let orig_buf = buf.clone();
    let res = op(buf);
    match res {
        Ok(Async::NotReady) | Err(_) => *buf = orig_buf,
        _ => {}
    }
    res
}

pub fn repeat<F>(buf: &mut EasyBuf, op: F) -> Poll<EasyBuf>
              where F: Fn(&[u8]) -> Poll<Option<usize>> {
    let len = try_ready!(repeat_len(buf.as_slice(), op));
    Ok(Async::Ready(buf.drain_to(len)))
}

pub fn repeat_len<F>(slice: &[u8], op: F) -> Poll<usize>
                  where F: Fn(&[u8]) -> Poll<Option<usize>> {
    let mut len = 0;
    loop {
        match try_ready!(op(&slice[len..])) {
            Some(add) => len += add,
            None => return Ok(Async::Ready(len))
        }
    }
}

//------------ Various Parsing -----------------------------------------------

pub fn octet(buf: &mut EasyBuf, octet: u8) -> Poll<()> {
    if buf.len() == 0 {
        Ok(Async::NotReady)
    }
    else if buf.as_slice()[0] == octet {
        buf.drain_to(1);
        Ok(Async::Ready(()))
    }
    else {
        Err(Error)
    }
}

/// Parses a literal sequence of octets.
///
/// If the start of `buf` matches `literal` case-insensitively, drains the
/// length of `literal` from the buffer and ready-returns.  If `buf` starts
/// with an incomplete match, returns non-ready. Otherwise returns an error.
pub fn literal(buf: &mut EasyBuf, literal: &[u8]) -> Poll<()> {
    use std::cmp::min;
    use std::ascii::AsciiExt;

    let litlen = {
        let len = buf.len();
        let litlen = literal.len();
        let minlen = min(len, litlen);
        let reduced = &buf.as_slice()[..minlen];
        let litreduced = &literal[..minlen];

        if !reduced.eq_ignore_ascii_case(litreduced) {
            return Err(Error)
        }
        else if minlen < litlen {
            return Ok(Async::NotReady)
        }
        litlen
    };
    let _ = buf.drain_to(litlen);
    Ok(Async::Ready(()))
}

/// Parses an escaped sequence of octets.
///
/// The closure `test` is given a bytes slice and is supposed to return the
/// number of octets at the beginnung of the slice form a single escaped
/// character.
///
/// Upon success, the function returns a buffer of the un-escaped characters.
pub fn escaped<F>(buf: &mut EasyBuf, test: F) -> Poll<EasyBuf>
               where F: Fn(&[u8]) -> Poll<usize> {
    let mut i = 0;
    while i < buf.len() {
        match test(&buf.as_slice()[i..]) {
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Ok(Async::Ready(count)) => {
                assert!(count > 0);
                i += count;
            }
            Err(err) => {
                if i > 0 {
                    return Ok(Async::Ready(buf.drain_to(i)));
                }
                else {
                    return Err(err)
                }
            }
        }
    }
    Ok(Async::NotReady)
}

//------------ Category Octet Parsing ----------------------------------------

/// Parses a single octet that matches a function.
///
/// If `test` returns `true` for first octet in `buf`, drains that octet from
/// `buf` and ready-returns it. If `buf` is empty returns non-ready. If there
/// is a first octet but `test` returns `false`, returns an error.
pub fn cat<F>(buf: &mut EasyBuf, test: F) -> Poll<u8>
           where F: FnOnce(u8) -> bool {
    if buf.len() < 1 {
        Ok(Async::NotReady)
    }
    else {
        let ch = buf.as_slice()[0];
        if test(ch) {
            let _ = buf.drain_to(1);
            Ok(Async::Ready(ch))
        }
        else {
            Err(Error)
        }
    }
}

/// Parses a non-empty sequence of octets matched by a function.
///
/// If `buf` starts with a sequence of at least one octet for which `test`
/// returns `true` which is followed by an octet for which `test` returns
/// `false`, drains the sequence from `buf` and ready-returns it. If there
/// is a sequence but no following octet, returns not ready. If the first
/// octet in `buf` is not matched by `test`, returns an error.
///
/// This function is used for parsing sequences in the middle of a message.
/// If the message can ends with the sequence, use `final_cats()` instead.
pub fn cats<F>(buf: &mut EasyBuf, test: F) -> Poll<EasyBuf>
            where F: Fn(u8) -> bool {
    if buf.len() < 1 {
        return Ok(Async::NotReady)
    }
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(0) => Err(Error),
        Some(end) => Ok(Async::Ready(buf.drain_to(end)))
    }
}

pub fn cats_len<F>(slice: &[u8], test: F) -> Poll<usize>
                where F: Fn(u8) -> bool {
    let mut i = 0;
    while i < slice.len() {
        if !test(slice[i]) {
            if i == 0 {
                return Err(Error)
            }
            else {
                return Ok(Async::Ready(i))
            }
        }
        i += 1;
    }
    Ok(Async::NotReady)
}

/// Parses a final non-empty sequence of octets matched by a function.
///
/// If `buf` consists entirly of a sequence of octets matched by `test`,
/// returns a clone of `buf`. Otherwise returns an error.
pub fn final_cats<F>(buf: &mut EasyBuf, test: F) -> Result<EasyBuf>
                  where F: Fn(u8) -> bool {
    let len = buf.len();
    if len == 0 {
        return Err(Error)
    }
    for item in buf.as_slice().iter() {
        if !test(*item) {
            return Err(Error);
        }
    }
    Ok(buf.drain_to(len))
}

/// Parses an optional sequence of octets matched by a function.
pub fn opt_cats<F>(buf: &mut EasyBuf, test: F) -> Poll<Option<EasyBuf>>
                where F: Fn(u8) -> bool {
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(0) => Ok(Async::Ready(None)),
        Some(end) => Ok(Async::Ready(Some(buf.drain_to(end))))
    }
}

/// Parses an option of at least `n` and at most `m` octets matched by `test`.
///
/// # Panics
///
/// The function panics if `m` is not greater than `n`.
pub fn nm_cats<F>(buf: &mut EasyBuf, n: usize, m: usize, test: F)
                  -> Poll<EasyBuf>
               where F: Fn(u8) -> bool {
    assert!(m > n);
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if !test(*item) {
            end = Some(index);
            break;
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(end) if end < n || end >= m => Err(Error),
        Some(end) => Ok(Async::Ready(buf.drain_to(end)))
    }
}

pub fn cats_cat<F>(buf: &mut EasyBuf, word: F, delim: F) -> Poll<EasyBuf>
                where F: Fn(u8) -> bool {
    if buf.len() < 2 {
        return Ok(Async::NotReady)
    }
    let mut end = None;
    for (index, item) in buf.as_slice().iter().enumerate() {
        if delim(*item) {
            end = Some(index);
            break;
        }
        else if !word(*item) {
            return Err(Error)
        }
    }
    match end {
        None => Ok(Async::NotReady),
        Some(end) => {
            let res = buf.drain_to(end);
            buf.drain_to(1);
            Ok(Async::Ready(res))
        }
    }
}

//============ Test =========================================================

#[cfg(test)]
mod test {
    use futures::Async;
    use tokio_core::io::EasyBuf;
    use super::*;

    fn buf(slice: &[u8]) -> EasyBuf { Vec::from(slice).into() }

    fn escape_seq(slice: &[u8]) -> Poll<usize> {
        assert!(!slice.is_empty());
        if slice[0] == 33 || (slice[0] >= 35 && slice[0] <= 91)
                          || (slice[0] >= 93 && slice[0] <= 126) {
            return Ok(Async::Ready(1))
        }
        if slice[0] == 92 {
            if slice.len() < 2 {
                return Ok(Async::NotReady)
            }
            if slice[0] >= 32 && slice[0] <= 126 {
                return Ok(Async::Ready(2))
            }
        }
        Err(Error)
    }

    #[test]
    fn test_escape() {
        assert_eq!(escaped(&mut buf(b"foo "), escape_seq),
                   Ok(Async::Ready(buf(b"foo"))));
        assert_eq!(escaped(&mut buf(b"f\\oo\\  "), escape_seq),
                   Ok(Async::Ready(buf(b"f\\oo\\ "))));
    }
}
<|endoftext|>


<commit_before>use super::config::{Config, Capture};
use std::collections::BTreeMap;
use std::io::{BufRead, Write};

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    LittleEndian,
    BigEndian,
    HexList,
}

struct Replacement {
    // Line for purposes of printing errors.
    line: usize,
    // Segment to add the value to.
    add_segment: usize,
    // The index in the add_segment to add the value.
    index: usize,
    // The tag to retrieve the position to add.
    tag: String,
    // The segment for which we are retrieving the position from the tag.
    pos_segment: usize,
    // The offset we add after retrieving the value from the tag.
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: BTreeMap<String, Vec<usize>>,
    replacements: Vec<Replacement>,
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: {
                let mut v = Vec::new();
                for _ in 0..config.segment_widths.len() {
                    v.push(Vec::new());
                }
                v
            },
            tags: BTreeMap::new(),
            replacements: Vec::new(),
        }
    }

    pub fn link(&mut self) {
        // Iterate through every replacement.
        for r in &self.replacements {
            // Get the tag offset vector corresponding to the replacement.
            let tag = self.tags.get(&r.tag).unwrap_or_else(|| {
                panic!("Error: Tag \"{}\" used on line {} never defined.",
                       r.tag,
                       r.line);
            });
            self.segments[r.add_segment][r.index] +=
                (tag[r.pos_segment] as isize + r.pos_offset) as u64;
        }
    }

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            // Remove everything after the first #, which denotes a comment.
            let line = line.splitn(2, '#').next().unwrap();

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    println!("Before: {}", self.segments[1].len());
                    self.parse_segment(word, index + 1);
                    println!("After: {}", self.segments[1].len());
                }
            } else {
                self.parse_segment(&line, index + 1);
            }
        }
    }

    pub fn output<W>(&self, format: OutputFormat, segment: usize, w: &mut W)
        where W: Write
    {
        use byteorder::{ByteOrder, LittleEndian, BigEndian};
        match format {
            OutputFormat::LittleEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    LittleEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::BigEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::HexList => {
                use std::iter::FromIterator;
                // Allocate enough bytes to store a u64
                let mut bytes = [0u8; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&(String::from_iter(bytes[(8 - width)..8]
                                .iter()
                                .map(|&b| format!("{:02X}", b))) +
                                     "\n")
                            .as_bytes())
                        .unwrap_or_else(|e| {
                            panic!("Error: Writing to output file for segment {} failed: {}",
                                   segment,
                                   e);
                        });
                }
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if segment.is_empty() {
            return;
        }
        if self.attempt_tag_create(segment) {
            return;
        }
        if self.attempt_rules(segment, line) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str) -> bool {
        if let Some(caps) = self.config.tag_create.regex.as_ref().unwrap().captures(segment) {
            self.tags.insert(caps.at(1).unwrap().to_string(),
                             self.segments
                                 .iter()
                                 .map(|v| v.len())
                                 .collect());
            true
        } else {
            false
        }
    }

    fn attempt_rules(&mut self, segment: &str, line: usize) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for self_reference in &rule.self_references {
                    segvals[self_reference.add_segment][self_reference.add_index] +=
                        (self.segments[self_reference.from_segment].len() as u64) <<
                        self_reference.shift;
                }
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    match capture {
                        &Capture::Tag { ref feedbacks } => {
                            for feedback in feedbacks {
                                self.replacements.push(Replacement {
                                    line: line,
                                    add_segment: feedback.add_segment,
                                    index: self.segments[feedback.add_segment].len() +
                                           feedback.add_index,
                                    tag: String::from(cap_string),
                                    pos_segment: feedback.from_segment,
                                    pos_offset: if feedback.relative {
                                        feedback.offset -
                                        self.segments[feedback.from_segment].len() as isize
                                    } else {
                                        feedback.offset
                                    },
                                });
                            }
                        }
                        &Capture::Str { add_segment } => {
                            for c in cap_string.chars() {
                                self.segments[add_segment].push(c as u64);
                                println!("Parsed: {}", c as u64);
                            }
                        }
                        &Capture::Num { ref feedbacks, ref base } => {
                            let pval = i64::from_str_radix(cap_string, *base).unwrap_or_else(|e| {
                                panic!("Error: Failed to parse captured string \"{}\" from \
                                        \"{}\": {}",
                                       cap_string,
                                       segment,
                                       e);
                            });
                            let val: u64 = unsafe { transmute(pval) };

                            for feedback in feedbacks {
                                let mut shiftval = val << feedback.shift;
                                if feedback.negate {
                                    shiftval = !shiftval + 1;
                                }
                                if feedback.fill {
                                    let baseval = segvals[feedback.segment][feedback.index];
                                    let fill_amount = shiftval as isize + feedback.fill_offset;
                                    if fill_amount.is_negative() {
                                        panic!("Error: Got a negative fill amount!");
                                    } else {
                                        if feedback.align {
                                            while self.segments[feedback.segment].len() <
                                                  fill_amount as usize {
                                                self.segments[feedback.segment].push(baseval);
                                            }
                                        } else {
                                            for _ in 0..fill_amount {
                                                self.segments[feedback.segment].push(baseval);
                                            }
                                        }
                                    }
                                    segvals[feedback.segment].pop();
                                } else {
                                    segvals[feedback.segment][index] += shiftval;
                                }
                            }
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<commit_msg>oops, remove debugging information, and last commit had string literals<commit_after>use super::config::{Config, Capture};
use std::collections::BTreeMap;
use std::io::{BufRead, Write};

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    LittleEndian,
    BigEndian,
    HexList,
}

struct Replacement {
    // Line for purposes of printing errors.
    line: usize,
    // Segment to add the value to.
    add_segment: usize,
    // The index in the add_segment to add the value.
    index: usize,
    // The tag to retrieve the position to add.
    tag: String,
    // The segment for which we are retrieving the position from the tag.
    pos_segment: usize,
    // The offset we add after retrieving the value from the tag.
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: BTreeMap<String, Vec<usize>>,
    replacements: Vec<Replacement>,
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: {
                let mut v = Vec::new();
                for _ in 0..config.segment_widths.len() {
                    v.push(Vec::new());
                }
                v
            },
            tags: BTreeMap::new(),
            replacements: Vec::new(),
        }
    }

    pub fn link(&mut self) {
        // Iterate through every replacement.
        for r in &self.replacements {
            // Get the tag offset vector corresponding to the replacement.
            let tag = self.tags.get(&r.tag).unwrap_or_else(|| {
                panic!("Error: Tag \"{}\" used on line {} never defined.",
                       r.tag,
                       r.line);
            });
            self.segments[r.add_segment][r.index] +=
                (tag[r.pos_segment] as isize + r.pos_offset) as u64;
        }
    }

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            // Remove everything after the first #, which denotes a comment.
            let line = line.splitn(2, '#').next().unwrap();

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    self.parse_segment(word, index + 1);
                }
            } else {
                self.parse_segment(&line, index + 1);
            }
        }
    }

    pub fn output<W>(&self, format: OutputFormat, segment: usize, w: &mut W)
        where W: Write
    {
        use byteorder::{ByteOrder, LittleEndian, BigEndian};
        match format {
            OutputFormat::LittleEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    LittleEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::BigEndian => {
                // Allocate enough bytes to store a u64
                let mut bytes = [0; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&bytes[0..width]).unwrap_or_else(|e| {
                        panic!("Error: Writing to output file for segment {} failed: {}",
                               segment,
                               e);
                    });
                }
            }
            OutputFormat::HexList => {
                use std::iter::FromIterator;
                // Allocate enough bytes to store a u64
                let mut bytes = [0u8; 8];
                let width = self.config.segment_widths[segment];
                for val in &self.segments[segment] {
                    BigEndian::write_u64(&mut bytes, *val);
                    w.write_all(&(String::from_iter(bytes[(8 - width)..8]
                                .iter()
                                .map(|&b| format!("{:02X}", b))) +
                                     "\n")
                            .as_bytes())
                        .unwrap_or_else(|e| {
                            panic!("Error: Writing to output file for segment {} failed: {}",
                                   segment,
                                   e);
                        });
                }
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if segment.is_empty() {
            return;
        }
        if self.attempt_tag_create(segment) {
            return;
        }
        if self.attempt_rules(segment, line) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str) -> bool {
        if let Some(caps) = self.config.tag_create.regex.as_ref().unwrap().captures(segment) {
            self.tags.insert(caps.at(1).unwrap().to_string(),
                             self.segments
                                 .iter()
                                 .map(|v| v.len())
                                 .collect());
            true
        } else {
            false
        }
    }

    fn attempt_rules(&mut self, segment: &str, line: usize) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for self_reference in &rule.self_references {
                    segvals[self_reference.add_segment][self_reference.add_index] +=
                        (self.segments[self_reference.from_segment].len() as u64) <<
                        self_reference.shift;
                }
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    match capture {
                        &Capture::Tag { ref feedbacks } => {
                            for feedback in feedbacks {
                                self.replacements.push(Replacement {
                                    line: line,
                                    add_segment: feedback.add_segment,
                                    index: self.segments[feedback.add_segment].len() +
                                           feedback.add_index,
                                    tag: String::from(cap_string),
                                    pos_segment: feedback.from_segment,
                                    pos_offset: if feedback.relative {
                                        feedback.offset -
                                        self.segments[feedback.from_segment].len() as isize
                                    } else {
                                        feedback.offset
                                    },
                                });
                            }
                        }
                        &Capture::Str { add_segment } => {
                            for c in cap_string.chars() {
                                self.segments[add_segment].push(c as u64);
                            }
                        }
                        &Capture::Num { ref feedbacks, ref base } => {
                            let pval = i64::from_str_radix(cap_string, *base).unwrap_or_else(|e| {
                                panic!("Error: Failed to parse captured string \"{}\" from \
                                        \"{}\": {}",
                                       cap_string,
                                       segment,
                                       e);
                            });
                            let val: u64 = unsafe { transmute(pval) };

                            for feedback in feedbacks {
                                let mut shiftval = val << feedback.shift;
                                if feedback.negate {
                                    shiftval = !shiftval + 1;
                                }
                                if feedback.fill {
                                    let baseval = segvals[feedback.segment][feedback.index];
                                    let fill_amount = shiftval as isize + feedback.fill_offset;
                                    if fill_amount.is_negative() {
                                        panic!("Error: Got a negative fill amount!");
                                    } else {
                                        if feedback.align {
                                            while self.segments[feedback.segment].len() <
                                                  fill_amount as usize {
                                                self.segments[feedback.segment].push(baseval);
                                            }
                                        } else {
                                            for _ in 0..fill_amount {
                                                self.segments[feedback.segment].push(baseval);
                                            }
                                        }
                                    }
                                    segvals[feedback.segment].pop();
                                } else {
                                    segvals[feedback.segment][index] += shiftval;
                                }
                            }
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<|endoftext|>


<commit_before>
use error::MpdResult;
use connection::{FromConn, MpdConnection, mpd_connection};
use songs::{MpdSong, MpdSongs, mpd_song};
use std::c_str::CString;
use libc::{c_uint, c_int, c_char};

extern {
    fn mpd_run_get_queue_song_pos(connection: *mut mpd_connection, pos: c_uint) -> *mut mpd_song;
    fn mpd_run_get_queue_song_id(connection: *mut mpd_connection, id: c_uint) -> *mut mpd_song;
    fn mpd_run_move_id(connection: *mut mpd_connection, from: c_uint, to: c_uint) -> bool;
    fn mpd_run_move(connection: *mut mpd_connection, from: c_uint, to: c_uint) -> bool;
    fn mpd_run_move_range(connection: *mut mpd_connection, start: c_uint, end: c_uint, to: c_uint) -> bool;
    fn mpd_run_shuffle(connection: *mut mpd_connection) -> bool;
    fn mpd_run_shuffle_range(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_swap_id(connection: *mut mpd_connection, id1: c_uint, id2: c_uint) -> bool;
    fn mpd_run_swap(connection: *mut mpd_connection, pos1: c_uint, pos2: c_uint) -> bool;
    fn mpd_run_add_id(connection: *mut mpd_connection, file: *const c_char) -> c_int;
    fn mpd_run_add_id_to(connection: *mut mpd_connection, uri: *const c_char, to: c_uint) -> c_int;
    fn mpd_send_list_queue_meta(connection: *mut mpd_connection) -> bool;
    fn mpd_send_list_queue_range_meta(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_delete(connection: *mut mpd_connection, pos: c_uint) -> bool;
    fn mpd_run_delete_range(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_delete_id(connection: *mut mpd_connection, id: c_uint) -> bool;
    fn mpd_run_clear(connection: *mut mpd_connection) -> bool;
}

pub struct MpdQueue<'a> {
    pub conn: &'a MpdConnection
}

impl<'a> MpdQueue<'a> {
    pub fn from_conn(conn: &'a MpdConnection) -> MpdQueue<'a> {
        MpdQueue { conn: conn }
    }

    /// Get song at some position in queue
    pub fn nth(&self, index: uint) -> MpdResult<MpdSong> {
        let song = unsafe { mpd_run_get_queue_song_pos(self.conn.conn, index as c_uint) };
        if song.is_null() {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(MpdSong { song: song })
        }
    }

    /// Get song by queue id
    pub fn get(&self, id: uint) -> MpdResult<MpdSong> {
        let song = unsafe { mpd_run_get_queue_song_id(self.conn.conn, id as c_uint) };
        if song.is_null() {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(MpdSong { song: song })
        }
    }

    /// Insert new song into queue at given position
    pub fn insert<T: ToSongUri>(&mut self, pos: uint, song: T) -> MpdResult<uint> {
        let uid = unsafe { mpd_run_add_id_to(self.conn.conn, song.song_uri().as_ptr(), pos as c_uint) };
        if uid < 0 {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(uid as uint)
        }
    }

    /// Add song at the end of the queue
    pub fn push<T: ToSongUri>(&mut self, song: T) -> MpdResult<uint> {
        let uid = unsafe { mpd_run_add_id(self.conn.conn, song.song_uri().as_ptr()) };
        if uid < 0 {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(uid as uint)
        }
    }

    /// Move song to some position in queue
    pub fn move_pos(&mut self, to: uint, from: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move(self.conn.conn, from as c_uint, to as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Move songs in given range
    pub fn move_range(&mut self, pos: uint, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move_range(self.conn.conn, start as c_uint, end as c_uint, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Move song to some position in queue by id
    pub fn move_to(&mut self, pos: uint, song: &MpdSong) -> MpdResult<()> {
        self.move_id(pos, song.id())
    }

    pub fn move_id(&mut self, pos: uint, id: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move_id(self.conn.conn, id as c_uint, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Swap two songs in given positions
    pub fn swap_pos(&mut self, song1: uint, song2: uint) -> MpdResult<()> {
        if unsafe { mpd_run_swap(self.conn.conn, song1 as c_uint, song2 as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Swap two songs in given positions by id
    pub fn swap(&mut self, song1: &MpdSong, song2: &MpdSong) -> MpdResult<()> {
        self.swap_id(song1.id(), song2.id())
    }

    pub fn swap_id(&mut self, song1: uint, song2: uint) -> MpdResult<()> {
        if unsafe { mpd_run_swap_id(self.conn.conn, song1 as c_uint, song2 as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    pub fn shuffle(&mut self) -> MpdResult<()> {
        if unsafe { mpd_run_shuffle(self.conn.conn) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    pub fn shuffle_range(&mut self, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_shuffle_range(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove a song
    pub fn remove(&mut self, song: &MpdSong) -> MpdResult<()> {
        self.remove_id(song.id())
    }

    pub fn remove_id(&mut self, id: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete_id(self.conn.conn, id as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove songs at given position
    pub fn remove_pos(&mut self, pos: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete(self.conn.conn, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove songs in given range
    pub fn remove_range(&mut self, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete_range(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Iterate over songs in the queue
    pub fn iter(&self) -> MpdResult<MpdSongs> {
        if unsafe { mpd_send_list_queue_meta(self.conn.conn) } {
            Ok(MpdSongs { conn: self.conn })
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Iterate over songs in given range
    pub fn iter_range(&self, start: uint, end: uint) -> MpdResult<MpdSongs> {
        if unsafe { mpd_send_list_queue_range_meta(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(MpdSongs { conn: self.conn })
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Length of the queue
    pub fn len(&self) -> MpdResult<uint> {
        self.conn.status().map(|s| s.queue_len())
    }

    /// Returns true if queue is empty
    pub fn is_empty(&self) -> MpdResult<bool> {
        self.len().map(|v| v == 0)
    }

    /// Clear queue
    pub fn clear(&mut self) -> MpdResult<()> {
        if unsafe { mpd_run_clear(self.conn.conn) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }
}

pub trait ToSongUri {
    fn song_uri(&self) -> CString;
}

impl ToSongUri for MpdSong {
    #[inline] fn song_uri(&self) -> CString {
        self.uri().to_c_str()
    }
}

impl<T: ToCStr> ToSongUri for T {
    #[inline] fn song_uri(&self) -> CString {
        self.to_c_str()
    }
}
<commit_msg>save and load playlist from queue<commit_after>
use error::MpdResult;
use connection::{FromConn, MpdConnection, mpd_connection};
use songs::{MpdSong, MpdSongs, mpd_song};
use playlists::MpdPlaylist;
use std::c_str::CString;
use libc::{c_uint, c_int, c_char};

extern {
    fn mpd_run_get_queue_song_pos(connection: *mut mpd_connection, pos: c_uint) -> *mut mpd_song;
    fn mpd_run_get_queue_song_id(connection: *mut mpd_connection, id: c_uint) -> *mut mpd_song;
    fn mpd_run_move_id(connection: *mut mpd_connection, from: c_uint, to: c_uint) -> bool;
    fn mpd_run_move(connection: *mut mpd_connection, from: c_uint, to: c_uint) -> bool;
    fn mpd_run_move_range(connection: *mut mpd_connection, start: c_uint, end: c_uint, to: c_uint) -> bool;
    fn mpd_run_shuffle(connection: *mut mpd_connection) -> bool;
    fn mpd_run_shuffle_range(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_swap_id(connection: *mut mpd_connection, id1: c_uint, id2: c_uint) -> bool;
    fn mpd_run_swap(connection: *mut mpd_connection, pos1: c_uint, pos2: c_uint) -> bool;
    fn mpd_run_add_id(connection: *mut mpd_connection, file: *const c_char) -> c_int;
    fn mpd_run_add_id_to(connection: *mut mpd_connection, uri: *const c_char, to: c_uint) -> c_int;
    fn mpd_send_list_queue_meta(connection: *mut mpd_connection) -> bool;
    fn mpd_send_list_queue_range_meta(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_delete(connection: *mut mpd_connection, pos: c_uint) -> bool;
    fn mpd_run_delete_range(connection: *mut mpd_connection, start: c_uint, end: c_uint) -> bool;
    fn mpd_run_delete_id(connection: *mut mpd_connection, id: c_uint) -> bool;
    fn mpd_run_clear(connection: *mut mpd_connection) -> bool;
    fn mpd_run_save(connection: *mut mpd_connection, name: *const c_char) -> bool;
    fn mpd_run_load(connection: *mut mpd_connection, name: *const c_char) -> bool;
}

pub struct MpdQueue<'a> {
    pub conn: &'a MpdConnection
}

impl<'a> MpdQueue<'a> {
    pub fn from_conn(conn: &'a MpdConnection) -> MpdQueue<'a> {
        MpdQueue { conn: conn }
    }

    /// Get song at some position in queue
    pub fn nth(&self, index: uint) -> MpdResult<MpdSong> {
        let song = unsafe { mpd_run_get_queue_song_pos(self.conn.conn, index as c_uint) };
        if song.is_null() {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(MpdSong { song: song })
        }
    }

    /// Get song by queue id
    pub fn get(&self, id: uint) -> MpdResult<MpdSong> {
        let song = unsafe { mpd_run_get_queue_song_id(self.conn.conn, id as c_uint) };
        if song.is_null() {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(MpdSong { song: song })
        }
    }

    /// Insert new song into queue at given position
    pub fn insert<T: ToSongUri>(&mut self, pos: uint, song: T) -> MpdResult<uint> {
        let uid = unsafe { mpd_run_add_id_to(self.conn.conn, song.song_uri().as_ptr(), pos as c_uint) };
        if uid < 0 {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(uid as uint)
        }
    }

    /// Add song at the end of the queue
    pub fn push<T: ToSongUri>(&mut self, song: T) -> MpdResult<uint> {
        let uid = unsafe { mpd_run_add_id(self.conn.conn, song.song_uri().as_ptr()) };
        if uid < 0 {
            Err(FromConn::from_conn(self.conn).unwrap())
        } else {
            Ok(uid as uint)
        }
    }

    /// Move song to some position in queue
    pub fn move_pos(&mut self, to: uint, from: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move(self.conn.conn, from as c_uint, to as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Move songs in given range
    pub fn move_range(&mut self, pos: uint, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move_range(self.conn.conn, start as c_uint, end as c_uint, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Move song to some position in queue by id
    pub fn move_to(&mut self, pos: uint, song: &MpdSong) -> MpdResult<()> {
        self.move_id(pos, song.id())
    }

    pub fn move_id(&mut self, pos: uint, id: uint) -> MpdResult<()> {
        if unsafe { mpd_run_move_id(self.conn.conn, id as c_uint, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Swap two songs in given positions
    pub fn swap_pos(&mut self, song1: uint, song2: uint) -> MpdResult<()> {
        if unsafe { mpd_run_swap(self.conn.conn, song1 as c_uint, song2 as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Swap two songs in given positions by id
    pub fn swap(&mut self, song1: &MpdSong, song2: &MpdSong) -> MpdResult<()> {
        self.swap_id(song1.id(), song2.id())
    }

    pub fn swap_id(&mut self, song1: uint, song2: uint) -> MpdResult<()> {
        if unsafe { mpd_run_swap_id(self.conn.conn, song1 as c_uint, song2 as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    pub fn shuffle(&mut self) -> MpdResult<()> {
        if unsafe { mpd_run_shuffle(self.conn.conn) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    pub fn shuffle_range(&mut self, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_shuffle_range(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove a song
    pub fn remove(&mut self, song: &MpdSong) -> MpdResult<()> {
        self.remove_id(song.id())
    }

    pub fn remove_id(&mut self, id: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete_id(self.conn.conn, id as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove songs at given position
    pub fn remove_pos(&mut self, pos: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete(self.conn.conn, pos as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Remove songs in given range
    pub fn remove_range(&mut self, start: uint, end: uint) -> MpdResult<()> {
        if unsafe { mpd_run_delete_range(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Iterate over songs in the queue
    pub fn iter(&self) -> MpdResult<MpdSongs> {
        if unsafe { mpd_send_list_queue_meta(self.conn.conn) } {
            Ok(MpdSongs { conn: self.conn })
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Iterate over songs in given range
    pub fn iter_range(&self, start: uint, end: uint) -> MpdResult<MpdSongs> {
        if unsafe { mpd_send_list_queue_range_meta(self.conn.conn, start as c_uint, end as c_uint) } {
            Ok(MpdSongs { conn: self.conn })
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Length of the queue
    pub fn len(&self) -> MpdResult<uint> {
        self.conn.status().map(|s| s.queue_len())
    }

    /// Returns true if queue is empty
    pub fn is_empty(&self) -> MpdResult<bool> {
        self.len().map(|v| v == 0)
    }

    /// Clear queue
    pub fn clear(&mut self) -> MpdResult<()> {
        if unsafe { mpd_run_clear(self.conn.conn) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Save queue into new playlist
    pub fn save(&mut self, name: &str) -> MpdResult<()> {
        if unsafe { mpd_run_save(self.conn.conn, name.to_c_str().as_ptr()) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Load queue from playlist
    pub fn load(&mut self, name: &str) -> MpdResult<()> {
        if unsafe { mpd_run_load(self.conn.conn, name.to_c_str().as_ptr()) } {
            Ok(())
        } else {
            Err(FromConn::from_conn(self.conn).unwrap())
        }
    }

    /// Load queue from playlist object
    #[inline] pub fn load_playlist(&mut self, pl: &MpdPlaylist) -> MpdResult<()> {
        self.load(pl.path()[])
    }
}

pub trait ToSongUri {
    fn song_uri(&self) -> CString;
}

impl ToSongUri for MpdSong {
    #[inline] fn song_uri(&self) -> CString {
        self.uri().to_c_str()
    }
}

impl<T: ToCStr> ToSongUri for T {
    #[inline] fn song_uri(&self) -> CString {
        self.to_c_str()
    }
}
<|endoftext|>


<commit_before>use regex_syntax::Expr;
use regex_syntax::Repeater;

use nfa::NFA;
use nfa::Spec;
use nfa::NFABuilder;
use nfa::Label;

struct Rule {
    regex: Expr,
    action: String,
}

/// Converts a sequence of characters into a concatenation of simple
/// transitions.
///
/// The return value is a Spec containing the concatenation of all
/// single transition automata generated for each character in `chars`.
fn literal_to_spec(chars: &Vec<char>) -> Spec {
    // take the first
    let mut tmp: Box<Spec> = Spec::single(Label::Symbol((*chars)[0]));

    // iterate for the others
    for i in chars.iter().skip(1) {
        let aux = Spec::single(Label::Symbol(*i));
        tmp = Spec::concat(tmp,aux);
    }

    // return the concatenation
    *tmp
}

/// Converts a vector of Expr into a concatenation of Specs.
///
/// The return value is a Spec containing the concatenation of all
/// Expr in the parameter vector.
fn concat_to_spec(exprs: &Vec<Expr>) -> Spec {
    let mut tmp: Box<Spec> = Box::new(regex_to_nfa_spec(&(exprs[0])));

    for i in exprs.iter().skip(1) {
        let aux : Box<Spec> = Box::new(regex_to_nfa_spec(&i));
        tmp  = Spec::concat(tmp,aux);
    }

    *tmp
}

/// Converts a vector of Expr into a union of Specs.
///
/// The return value is a Spec containing the union of all
/// Expr in the parameter vector.
fn alternate_to_spec(exprs: &Vec<Expr>) -> Spec {
    let mut tmp: Box<Spec> = Box::new(regex_to_nfa_spec(&(exprs[0])));;

    for i in exprs.iter().skip(1) {
        let aux : Box<Spec> = Box::new(regex_to_nfa_spec(&i));
        tmp  = Spec::union(tmp,aux);
    }

    *tmp
}

fn repeater_range(s1: Box<Spec>, min: u32, max: Option<u32>) -> Spec {
    let mut begin:u32 = 0;
    let mut aux:Box<Spec>;
    let mut spec:Box<Spec>;

    if min == 0 {
        aux = Spec::single(Label::Epsilon);
    } else {
        aux = s1.clone();
        begin = 1;
    }

    for i in begin..min {
        aux = Spec::concat(aux, s1.clone());
    }

    if max == None {
        spec = Spec::concat(aux, Spec::star(s1))
    } else {
        spec = aux.clone();

        //iterate min + 1 ... max
        for i in min..max.unwrap() {
            aux = Spec::concat(aux, s1.clone());
            spec = Spec::union(spec, aux.clone());
        }
    }
    *spec
}

/// Converts `re` and repeater specification `r` into a NFA `Spec`.
///
/// The repetition behavior is translated to the basic constructs
/// of a NFA `Spec`: union, concatenation and Kleene star of NFAs,
/// using the NFA built for `re` as one of the operands.
fn repeater_to_spec(re: &Expr, r: &Repeater) -> Spec {
    let s1 = Box::new(regex_to_nfa_spec(re));

    match *r {
        Repeater::ZeroOrOne => Spec::Union(Spec::single(Label::Epsilon), s1),
        Repeater::ZeroOrMore => Spec::Star(s1),
        Repeater::OneOrMore => Spec::Concat(s1.clone(), Spec::star(s1)),
        Repeater::Range { min, max } => repeater_range(s1, min, max)
    }
}

/// Converts the regexp `re` into a NFA `Spec`.
///
/// The function traverses the `re` syntax tree recursively, building
/// NFA Specs for each component and combining them according to the
/// operations in the ER.
pub fn regex_to_nfa_spec(re: &Expr) -> Spec {
    match *re {
        Expr::AnyChar => Spec::Single(Label::Any),
        Expr::AnyCharNoNL => Spec::Single(Label::Any),  // FIX: don't match newline
        Expr::Repeat { ref e, r, greedy } => repeater_to_spec(&*e, &r),
        Expr::Literal{ ref chars, casei} => literal_to_spec(chars),
        Expr::Concat(ref exprs) => concat_to_spec(&exprs),
        Expr::Alternate(ref exprs) => alternate_to_spec(&exprs),
        _ => unimplemented!()
    }
}

#[test]
fn test_repeater_zero_or_more1() {
    let re = Expr::parse(r".*").unwrap();
    let spec = regex_to_nfa_spec(&re);

    println!("regex: {:?}", re);
    assert_eq!(spec, *Spec::star(Spec::single(Label::Any)));

}

#[test]
fn test_literal() {
    let re = Expr::parse(r"ab").unwrap();
    let spec = regex_to_nfa_spec(&re);

    println!("regex: {:?}", re);
    assert_eq!(spec, *Spec::concat(Spec::single(Label::Symbol('a')), Spec::single(Label::Symbol('b'))));
}

#[test]
fn test_concat(){
    let re1 = Expr::parse(r"ab").unwrap();
    let re2 = Expr::parse(r"cd").unwrap();

    let mut tmp: Vec<Expr> = Vec::with_capacity(2);

    tmp.push(re1);
    tmp.push(re2);

    let aux = concat_to_spec(&tmp);

    let aux2 = NFABuilder::build_from_spec(aux);

    assert!(aux2.simulate("abcd"));
    assert!(!aux2.simulate("ab"));
    assert!(!aux2.simulate("cd"));

}

#[test]
fn test_altenate(){
    let re1 = Expr::parse(r"ab").unwrap();
    let re2 = Expr::parse(r"cd").unwrap();

    let mut tmp: Vec<Expr> = Vec::with_capacity(2);

    tmp.push(re1);
    tmp.push(re2);
    let aux = alternate_to_spec(&tmp);

    let aux2 = NFABuilder::build_from_spec(aux);

    assert!(!aux2.simulate("abcd"));
    assert!(aux2.simulate("ab"));
    assert!(aux2.simulate("cd"));
}

#[test]
fn test_repeater_range() {
    let re = Expr::parse(r"a{0,4}").unwrap();
    let spec = regex_to_nfa_spec(&re);
    let n = NFABuilder::build_from_spec(spec);

    println!("regex: {:?}", re);
    assert!(n.simulate("aaaa"));
    assert!(n.simulate(""));
    assert!(!n.simulate("aaaaaaaaaaaa"));
}
<commit_msg>Minor improvements in code and doc comments<commit_after>use regex_syntax::Expr;
use regex_syntax::Repeater;

use nfa::NFA;
use nfa::Spec;
use nfa::NFABuilder;
use nfa::Label;

struct Rule {
    regex: Expr,
    action: String,
}

/// Converts a sequence of characters into a concatenation of simple
/// transitions.
///
/// The return value is a Spec containing the concatenation of all
/// single transition automata generated for each character in `chars`.
fn literal_to_spec(chars: &Vec<char>) -> Spec {
    // take the first
    let mut tmp: Box<Spec> = Spec::single(Label::Symbol((*chars)[0]));

    // iterate for the others
    for i in chars.iter().skip(1) {
        let aux = Spec::single(Label::Symbol(*i));
        tmp = Spec::concat(tmp,aux);
    }

    // return the concatenation
    *tmp
}

/// Converts a concatenation of `Expr` into a concatenation of `Spec`s.
///
/// The return value is a `Spec` containing the concatenation of all
/// REs in `exprs`.
fn concat_to_spec(exprs: &Vec<Expr>) -> Spec {
    let mut tmp: Box<Spec> = Box::new(regex_to_nfa_spec(&(exprs[0])));

    for i in exprs.iter().skip(1) {
        let aux : Box<Spec> = Box::new(regex_to_nfa_spec(&i));
        tmp  = Spec::concat(tmp,aux);
    }

    *tmp
}

/// Converts a union of `Expr` into a union of `Spec`s.
///
/// The return value is a `Spec` containing the union of all
/// REs in `exprs`.
fn alternate_to_spec(exprs: &Vec<Expr>) -> Spec {
    let mut tmp: Box<Spec> = Box::new(regex_to_nfa_spec(&(exprs[0])));;

    for i in exprs.iter().skip(1) {
        let aux : Box<Spec> = Box::new(regex_to_nfa_spec(&i));
        tmp  = Spec::union(tmp,aux);
    }

    *tmp
}

fn repeater_range(s1: Box<Spec>, min: u32, max: Option<u32>) -> Spec {
    let mut begin:u32 = 0;
    let mut aux:Box<Spec>;
    let mut spec:Box<Spec>;

    if min == 0 {
        aux = Spec::single(Label::Epsilon);
    } else {
        aux = s1.clone();
        begin = 1;
    }

    for _ in begin..min {
        aux = Spec::concat(aux, s1.clone());
    }

    if max == None {
        spec = Spec::concat(aux, Spec::star(s1))
    } else {
        spec = aux.clone();

        //iterate min + 1 ... max
        for _ in min..max.unwrap() {
            aux = Spec::concat(aux, s1.clone());
            spec = Spec::union(spec, aux.clone());
        }
    }
    *spec
}

/// Converts `re` and repeater specification `r` into a NFA `Spec`.
///
/// The repetition behavior is translated to the basic constructs
/// of a NFA `Spec`: union, concatenation and Kleene star of NFAs,
/// using the NFA built for `re` as one of the operands.
fn repeater_to_spec(re: &Expr, r: &Repeater) -> Spec {
    let s1 = Box::new(regex_to_nfa_spec(re));

    match *r {
        Repeater::ZeroOrOne => Spec::Union(Spec::single(Label::Epsilon), s1),
        Repeater::ZeroOrMore => Spec::Star(s1),
        Repeater::OneOrMore => Spec::Concat(s1.clone(), Spec::star(s1)),
        Repeater::Range { min, max } => repeater_range(s1, min, max)
    }
}

/// Converts the regexp `re` into a NFA `Spec`.
///
/// The function traverses the `re` syntax tree recursively, building
/// NFA Specs for each component and combining them according to the
/// operations in the ER.
pub fn regex_to_nfa_spec(re: &Expr) -> Spec {
    match *re {
        Expr::AnyChar => Spec::Single(Label::Any),
        Expr::AnyCharNoNL => Spec::Single(Label::Any),  // FIX: don't match newline
        Expr::Repeat { ref e, r, greedy } => repeater_to_spec(&*e, &r),
        Expr::Literal{ ref chars, casei } => literal_to_spec(chars),
        Expr::Concat(ref exprs) => concat_to_spec(&exprs),
        Expr::Alternate(ref exprs) => alternate_to_spec(&exprs),
        _ => unimplemented!()
    }
}

#[test]
fn test_repeater_zero_or_more1() {
    let re = Expr::parse(r".*").unwrap();
    let spec = regex_to_nfa_spec(&re);

    println!("regex: {:?}", re);
    assert_eq!(spec, *Spec::star(Spec::single(Label::Any)));

}

#[test]
fn test_literal() {
    let re = Expr::parse(r"ab").unwrap();
    let spec = regex_to_nfa_spec(&re);

    println!("regex: {:?}", re);
    assert_eq!(spec, *Spec::concat(Spec::single(Label::Symbol('a')), Spec::single(Label::Symbol('b'))));
}

#[test]
fn test_concat(){
    let re1 = Expr::parse(r"ab").unwrap();
    let re2 = Expr::parse(r"cd").unwrap();

    let mut tmp: Vec<Expr> = Vec::with_capacity(2);

    tmp.push(re1);
    tmp.push(re2);

    let aux = concat_to_spec(&tmp);

    let aux2 = NFABuilder::build_from_spec(aux);

    assert!(aux2.simulate("abcd"));
    assert!(!aux2.simulate("ab"));
    assert!(!aux2.simulate("cd"));

}

#[test]
fn test_altenate(){
    let re1 = Expr::parse(r"ab").unwrap();
    let re2 = Expr::parse(r"cd").unwrap();

    let mut tmp: Vec<Expr> = Vec::with_capacity(2);

    tmp.push(re1);
    tmp.push(re2);
    let aux = alternate_to_spec(&tmp);

    let aux2 = NFABuilder::build_from_spec(aux);

    assert!(!aux2.simulate("abcd"));
    assert!(aux2.simulate("ab"));
    assert!(aux2.simulate("cd"));
}

#[test]
fn test_repeater_range() {
    let re = Expr::parse(r"a{0,4}").unwrap();
    let spec = regex_to_nfa_spec(&re);
    let n = NFABuilder::build_from_spec(spec);

    println!("regex: {:?}", re);
    assert!(n.simulate("aaaa"));
    assert!(n.simulate(""));
    assert!(!n.simulate("aaaaaaaaaaaa"));
}
<|endoftext|>


<commit_before>use mio_uds::UnixStream;
use mio::Poll;
use libc::{self,c_char,uint32_t,int32_t,pid_t};
use std::io;
use std::ffi::CString;
use std::iter::repeat;
use std::ptr::null_mut;
use std::process::Command;
use std::os::unix::process::CommandExt;
use std::os::unix::io::{AsRawFd,FromRawFd};
use nix::unistd::*;
use nix::fcntl::{fcntl,FcntlArg,FdFlag,FD_CLOEXEC};

use sozu::channel::Channel;
use sozu::network::proxy::Server;
use sozu::network::session::Session;
use sozu::network::{ProxyOrder,ServerMessage,http,tls};
use sozu_command::data::ProxyType;
use sozu_command::config::{Config,ProxyConfig};

use logging;
use command::Worker;

pub fn start_workers(config: &Config) -> Vec<Worker> {
  let mut workers = Vec::new();
  for index in 0..config.worker_count.unwrap_or(1) {
    let (pid, command) = start_worker_process(&index.to_string(), config);
    let w =  Worker::new(index as u32, pid, command, config);
    workers.push(w);
  }
  workers
}

pub fn start_worker(id: u32, config: &Config) -> Option<Worker> {
  let (pid, command) = start_worker_process(&id.to_string(), config);
  let w =  Worker::new(id, pid, command, config);
  Some(w)
}

fn generate_channels() -> io::Result<(Channel<ProxyOrder,ServerMessage>, Channel<ServerMessage,ProxyOrder>)> {
  let (command,proxy) = try!(UnixStream::pair());
  //FIXME: configurable buffer size
  let proxy_channel   = Channel::new(proxy, 10000, 20000);
  let command_channel = Channel::new(command, 10000, 20000);
  Ok((command_channel, proxy_channel))
}

pub fn begin_worker_process(fd: i32, id: &str, channel_buffer_size: usize) {
  let mut command: Channel<ServerMessage,Config> = Channel::new(
    unsafe { UnixStream::from_raw_fd(fd) },
    channel_buffer_size,
    channel_buffer_size * 2
  );

  command.set_nonblocking(false);

  let proxy_config = command.read_message().expect("worker could not read configuration from socket");
  //println!("got message: {:?}", proxy_config);

  logging::setup(format!("{}-{}", "TAG", id), &proxy_config.log_level, &proxy_config.log_target);

  command.set_nonblocking(true);
  let command: Channel<ServerMessage,ProxyOrder> = command.into();

  let mut event_loop  = Poll::new().expect("could not create event loop");

  let http_session = proxy_config.http.and_then(|conf| conf.to_http()).and_then(|http_conf| {
    let max_connections = http_conf.max_connections;
    let max_listeners = 1;
    http::ServerConfiguration::new(http_conf, &mut event_loop, 1 + max_listeners).map(|configuration| {
      Session::new(1, max_connections, 0, configuration, &mut event_loop)
    }).ok()
  });

  let https_session = proxy_config.https.and_then(|conf| conf.to_tls()).and_then(|https_conf| {
    let max_connections = https_conf.max_connections;
    let max_listeners   = 1;
    tls::ServerConfiguration::new(https_conf, 6148914691236517205, &mut event_loop, 1 + max_listeners).map(|configuration| {
      Session::new(max_listeners, max_connections, 6148914691236517205, configuration, &mut event_loop)
    }).ok()
  });
  //TODO: implement for TCP

  let mut server = Server::new(event_loop, command, http_session, https_session, None);
  info!("starting event loop");
  server.run();
  info!("ending event loop");
}

pub fn start_worker_process(id: &str, config: &Config) -> (pid_t, Channel<ProxyOrder,ServerMessage>) {
  trace!("parent({})", unsafe { libc::getpid() });

  let (server, client) = UnixStream::pair().unwrap();

  // FD_CLOEXEC is set by default on every fd in Rust standard lib,
  // so we need to remove the flag on the client, otherwise
  // it won't be accessible
  let cl_flags = fcntl(client.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
  let mut new_cl_flags = FdFlag::from_bits(cl_flags).unwrap();
  new_cl_flags.remove(FD_CLOEXEC);
  fcntl(client.as_raw_fd(), FcntlArg::F_SETFD(new_cl_flags));

  let channel_buffer_size = config.channel_buffer_size.unwrap_or(10000);
  let channel_max_buffer_size = channel_buffer_size * 2;

  let mut command: Channel<Config,ServerMessage> = Channel::new(
    server,
    channel_buffer_size,
    channel_max_buffer_size
  );
  command.set_nonblocking(false);

  let path = unsafe { get_executable_path() };

  info!("launching worker");
  //FIXME: remove the expect, return a result?
  match fork().expect("fork failed") {
    ForkResult::Parent{ child } => {
      info!("worker launched: {}", child);
      command.write_message(config);
      command.set_nonblocking(true);

      let command: Channel<ProxyOrder,ServerMessage> = command.into();
      return (child, command);
    }
    ForkResult::Child => {
      trace!("child({}):\twill spawn a child", unsafe { libc::getpid() });
      Command::new(path.to_str().unwrap())
        .arg("worker")
        .arg("--fd")
        .arg(client.as_raw_fd().to_string())
        .arg("--id")
        .arg(id)
        .arg("--channel-buffer-size")
        .arg(channel_buffer_size.to_string())
        .exec();

      unreachable!();
    }
  }
}

#[cfg(target_os = "linux")]
pub unsafe fn get_executable_path() -> CString {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let mut pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let proc_path = CString::new("/proc/self/exe").unwrap();
  let sz = libc::readlink( proc_path.as_ptr(), ptr, 1999);
  let path = CString::from_raw(ptr);
  path
}

#[cfg(target_os = "macos")]
extern {
  pub fn _NSGetExecutablePath(buf: *mut c_char, size: *mut uint32_t) -> int32_t;
}

#[cfg(target_os = "macos")]
pub unsafe fn get_executable_path() -> CString {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let mut size:uint32_t = capacity as u32;
  if _NSGetExecutablePath(ptr, &mut size) == 0 {

    let mut temp2:Vec<u8> = Vec::with_capacity(capacity);
    temp2.extend(repeat(0).take(capacity));
    let pathbuf2 = CString::from_vec_unchecked(temp2);
    let ptr2 = pathbuf2.into_raw();

    if libc::realpath(ptr, ptr2) != null_mut() {
      let path = CString::from_raw(ptr2);
      path
    } else {
      panic!();
    }
  } else {
    panic!("buffer too small");
  }
}
<commit_msg>HTTPS tokens do not start at 0<commit_after>use mio_uds::UnixStream;
use mio::Poll;
use libc::{self,c_char,uint32_t,int32_t,pid_t};
use std::io;
use std::ffi::CString;
use std::iter::repeat;
use std::ptr::null_mut;
use std::process::Command;
use std::os::unix::process::CommandExt;
use std::os::unix::io::{AsRawFd,FromRawFd};
use nix::unistd::*;
use nix::fcntl::{fcntl,FcntlArg,FdFlag,FD_CLOEXEC};

use sozu::channel::Channel;
use sozu::network::proxy::Server;
use sozu::network::session::Session;
use sozu::network::{ProxyOrder,ServerMessage,http,tls};
use sozu_command::data::ProxyType;
use sozu_command::config::{Config,ProxyConfig};

use logging;
use command::Worker;

pub fn start_workers(config: &Config) -> Vec<Worker> {
  let mut workers = Vec::new();
  for index in 0..config.worker_count.unwrap_or(1) {
    let (pid, command) = start_worker_process(&index.to_string(), config);
    let w =  Worker::new(index as u32, pid, command, config);
    workers.push(w);
  }
  workers
}

pub fn start_worker(id: u32, config: &Config) -> Option<Worker> {
  let (pid, command) = start_worker_process(&id.to_string(), config);
  let w =  Worker::new(id, pid, command, config);
  Some(w)
}

fn generate_channels() -> io::Result<(Channel<ProxyOrder,ServerMessage>, Channel<ServerMessage,ProxyOrder>)> {
  let (command,proxy) = try!(UnixStream::pair());
  //FIXME: configurable buffer size
  let proxy_channel   = Channel::new(proxy, 10000, 20000);
  let command_channel = Channel::new(command, 10000, 20000);
  Ok((command_channel, proxy_channel))
}

pub fn begin_worker_process(fd: i32, id: &str, channel_buffer_size: usize) {
  let mut command: Channel<ServerMessage,Config> = Channel::new(
    unsafe { UnixStream::from_raw_fd(fd) },
    channel_buffer_size,
    channel_buffer_size * 2
  );

  command.set_nonblocking(false);

  let proxy_config = command.read_message().expect("worker could not read configuration from socket");
  //println!("got message: {:?}", proxy_config);

  logging::setup(format!("{}-{}", "TAG", id), &proxy_config.log_level, &proxy_config.log_target);

  command.set_nonblocking(true);
  let command: Channel<ServerMessage,ProxyOrder> = command.into();

  let mut event_loop  = Poll::new().expect("could not create event loop");

  let http_session = proxy_config.http.and_then(|conf| conf.to_http()).and_then(|http_conf| {
    let max_connections = http_conf.max_connections;
    let max_listeners = 1;
    http::ServerConfiguration::new(http_conf, &mut event_loop, 1 + max_listeners).map(|configuration| {
      Session::new(1, max_connections, 0, configuration, &mut event_loop)
    }).ok()
  });

  let https_session = proxy_config.https.and_then(|conf| conf.to_tls()).and_then(|https_conf| {
    let max_connections = https_conf.max_connections;
    let max_listeners   = 1;
    tls::ServerConfiguration::new(https_conf, 6148914691236517205, &mut event_loop, 1 + max_listeners + 6148914691236517205).map(|configuration| {
      Session::new(max_listeners, max_connections, 6148914691236517205, configuration, &mut event_loop)
    }).ok()
  });
  //TODO: implement for TCP

  let mut server = Server::new(event_loop, command, http_session, https_session, None);
  info!("starting event loop");
  server.run();
  info!("ending event loop");
}

pub fn start_worker_process(id: &str, config: &Config) -> (pid_t, Channel<ProxyOrder,ServerMessage>) {
  trace!("parent({})", unsafe { libc::getpid() });

  let (server, client) = UnixStream::pair().unwrap();

  // FD_CLOEXEC is set by default on every fd in Rust standard lib,
  // so we need to remove the flag on the client, otherwise
  // it won't be accessible
  let cl_flags = fcntl(client.as_raw_fd(), FcntlArg::F_GETFD).unwrap();
  let mut new_cl_flags = FdFlag::from_bits(cl_flags).unwrap();
  new_cl_flags.remove(FD_CLOEXEC);
  fcntl(client.as_raw_fd(), FcntlArg::F_SETFD(new_cl_flags));

  let channel_buffer_size = config.channel_buffer_size.unwrap_or(10000);
  let channel_max_buffer_size = channel_buffer_size * 2;

  let mut command: Channel<Config,ServerMessage> = Channel::new(
    server,
    channel_buffer_size,
    channel_max_buffer_size
  );
  command.set_nonblocking(false);

  let path = unsafe { get_executable_path() };

  info!("launching worker");
  //FIXME: remove the expect, return a result?
  match fork().expect("fork failed") {
    ForkResult::Parent{ child } => {
      info!("worker launched: {}", child);
      command.write_message(config);
      command.set_nonblocking(true);

      let command: Channel<ProxyOrder,ServerMessage> = command.into();
      return (child, command);
    }
    ForkResult::Child => {
      trace!("child({}):\twill spawn a child", unsafe { libc::getpid() });
      Command::new(path.to_str().unwrap())
        .arg("worker")
        .arg("--fd")
        .arg(client.as_raw_fd().to_string())
        .arg("--id")
        .arg(id)
        .arg("--channel-buffer-size")
        .arg(channel_buffer_size.to_string())
        .exec();

      unreachable!();
    }
  }
}

#[cfg(target_os = "linux")]
pub unsafe fn get_executable_path() -> CString {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let mut pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let proc_path = CString::new("/proc/self/exe").unwrap();
  let sz = libc::readlink( proc_path.as_ptr(), ptr, 1999);
  let path = CString::from_raw(ptr);
  path
}

#[cfg(target_os = "macos")]
extern {
  pub fn _NSGetExecutablePath(buf: *mut c_char, size: *mut uint32_t) -> int32_t;
}

#[cfg(target_os = "macos")]
pub unsafe fn get_executable_path() -> CString {
  let capacity = 2000;
  let mut temp:Vec<u8> = Vec::with_capacity(capacity);
  temp.extend(repeat(0).take(capacity));
  let pathbuf = CString::from_vec_unchecked(temp);
  let ptr = pathbuf.into_raw();

  let mut size:uint32_t = capacity as u32;
  if _NSGetExecutablePath(ptr, &mut size) == 0 {

    let mut temp2:Vec<u8> = Vec::with_capacity(capacity);
    temp2.extend(repeat(0).take(capacity));
    let pathbuf2 = CString::from_vec_unchecked(temp2);
    let ptr2 = pathbuf2.into_raw();

    if libc::realpath(ptr, ptr2) != null_mut() {
      let path = CString::from_raw(ptr2);
      path
    } else {
      panic!();
    }
  } else {
    panic!("buffer too small");
  }
}
<|endoftext|>


<commit_before>//! Error types.

use std::error::{self, Error};
use std::fmt;

use cairo;
use cssparser::{BasicParseError, BasicParseErrorKind};
use glib;
use markup5ever::QualName;

use crate::allowed_url::Fragment;
use crate::node::RsvgNode;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(
                f,
                "parse error: {}",
                s
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value: {}",
                s
            ),
        }
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, location: _ } =  e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    HrefError(HrefError),
    CannotLookupExternalReferences,
    NotFound,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
    InvalidId(DefsLookupErrorKind),
    InvalidHref,
    OutOfMemory,
    HandleIsNotLoaded,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(RsvgNode),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) =>
                write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) =>
                write!(f, "link {} is to object of invalid type", frag),

            AcquireError::CircularReference(ref node) =>
                write!(f, "circular reference in node {}", node),

            AcquireError::MaxReferencesExceeded =>
                write!(f, "maximum number of references exceeded"),
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
///
/// A `NodeError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```ignore
/// use error::AttributeResultExt;
///
/// // fn parse_foo(...) -> Result<Foo, FooError>
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
///
/// self.foo = parse_foo(value).attribute(local_name!("foo"))?;
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `NodeError` with the provided `attr`.
pub trait AttributeResultExt<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O, E> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError> {
        self.map_err(|e| e.into())
            .map_err(|err| NodeError { attr, err })
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    NoDataPassedToParser,
    XmlParseError(String),
    // Could not parse data: URL
    CouldNotCreateXmlParser,
    BadUrl,
    BadDataUrl,
    BadStylesheet,
    BadCss,
    Cairo(cairo::Status),
    EmptyData,
    SvgHasNoElements,
    RootElementIsNotSvg,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::NoDataPassedToParser => "no data passed to parser",
            LoadingError::CouldNotCreateXmlParser => "could not create XML parser",
            LoadingError::XmlParseError(_) => "XML parse error",
            LoadingError::BadUrl => "invalid URL",
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::BadStylesheet => "invalid stylesheet",
            LoadingError::BadCss => "invalid CSS",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::SvgHasNoElements => "SVG has no elements",
            LoadingError::RootElementIsNotSvg => "root element is not <svg>",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::NoDataPassedToParser
            | LoadingError::CouldNotCreateXmlParser
            | LoadingError::BadUrl
            | LoadingError::BadDataUrl
            | LoadingError::BadStylesheet
            | LoadingError::BadCss
            | LoadingError::EmptyData
            | LoadingError::SvgHasNoElements
            | LoadingError::RootElementIsNotSvg
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl error::Error for RenderingError {
    fn description(&self) -> &str {
        match *self {
            RenderingError::Cairo(_) => "cairo error",
            RenderingError::CircularReference => "circular reference",
            RenderingError::InstancingLimit => "instancing limit",
            RenderingError::InvalidId(_) => "invalid id",
            RenderingError::InvalidHref => "invalid href",
            RenderingError::OutOfMemory => "out of memory",
            RenderingError::HandleIsNotLoaded => "SVG data is not loaded into handle",
        }
    }
}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
            RenderingError::CircularReference
            | RenderingError::InstancingLimit
            | RenderingError::InvalidHref
            | RenderingError::OutOfMemory
            | RenderingError::HandleIsNotLoaded => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}
<commit_msg>Remove unneeded type parameter E on AttributeResultExt<commit_after>//! Error types.

use std::error::{self, Error};
use std::fmt;

use cairo;
use cssparser::{BasicParseError, BasicParseErrorKind};
use glib;
use markup5ever::QualName;

use crate::allowed_url::Fragment;
use crate::node::RsvgNode;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(
                f,
                "parse error: {}",
                s
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value: {}",
                s
            ),
        }
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, location: _ } =  e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    HrefError(HrefError),
    CannotLookupExternalReferences,
    NotFound,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
    InvalidId(DefsLookupErrorKind),
    InvalidHref,
    OutOfMemory,
    HandleIsNotLoaded,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(RsvgNode),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) =>
                write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) =>
                write!(f, "link {} is to object of invalid type", frag),

            AcquireError::CircularReference(ref node) =>
                write!(f, "circular reference in node {}", node),

            AcquireError::MaxReferencesExceeded =>
                write!(f, "maximum number of references exceeded"),
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
///
/// A `NodeError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```ignore
/// use error::AttributeResultExt;
///
/// // fn parse_foo(...) -> Result<Foo, FooError>
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
///
/// self.foo = parse_foo(value).attribute(local_name!("foo"))?;
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `NodeError` with the provided `attr`.
pub trait AttributeResultExt<O> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError> {
        self.map_err(|e| e.into())
            .map_err(|err| NodeError { attr, err })
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    NoDataPassedToParser,
    XmlParseError(String),
    // Could not parse data: URL
    CouldNotCreateXmlParser,
    BadUrl,
    BadDataUrl,
    BadStylesheet,
    BadCss,
    Cairo(cairo::Status),
    EmptyData,
    SvgHasNoElements,
    RootElementIsNotSvg,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::NoDataPassedToParser => "no data passed to parser",
            LoadingError::CouldNotCreateXmlParser => "could not create XML parser",
            LoadingError::XmlParseError(_) => "XML parse error",
            LoadingError::BadUrl => "invalid URL",
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::BadStylesheet => "invalid stylesheet",
            LoadingError::BadCss => "invalid CSS",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::SvgHasNoElements => "SVG has no elements",
            LoadingError::RootElementIsNotSvg => "root element is not <svg>",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::NoDataPassedToParser
            | LoadingError::CouldNotCreateXmlParser
            | LoadingError::BadUrl
            | LoadingError::BadDataUrl
            | LoadingError::BadStylesheet
            | LoadingError::BadCss
            | LoadingError::EmptyData
            | LoadingError::SvgHasNoElements
            | LoadingError::RootElementIsNotSvg
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl error::Error for RenderingError {
    fn description(&self) -> &str {
        match *self {
            RenderingError::Cairo(_) => "cairo error",
            RenderingError::CircularReference => "circular reference",
            RenderingError::InstancingLimit => "instancing limit",
            RenderingError::InvalidId(_) => "invalid id",
            RenderingError::InvalidHref => "invalid href",
            RenderingError::OutOfMemory => "out of memory",
            RenderingError::HandleIsNotLoaded => "SVG data is not loaded into handle",
        }
    }
}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
            RenderingError::CircularReference
            | RenderingError::InstancingLimit
            | RenderingError::InvalidHref
            | RenderingError::OutOfMemory
            | RenderingError::HandleIsNotLoaded => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}
<|endoftext|>