<commit_before>//! Shadowsocks Local server serving on a Tun interface

#[cfg(unix)]
use std::os::unix::io::RawFd;
use std::{
    io::{self, Cursor, ErrorKind},
    net::{Ipv4Addr, Ipv6Addr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use byte_string::ByteStr;
use bytes::BytesMut;
use etherparse::{IpHeader, PacketHeaders, ReadError, TransportHeader};
use futures::future;
use ipnet::{IpNet, Ipv4Net};
use log::{error, info, trace, warn};
use shadowsocks::config::Mode;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tun::{AsyncDevice, Configuration as TunConfiguration, Device, Error as TunError, Layer};

use crate::local::{context::ServiceContext, loadbalancing::PingBalancer};

use self::{
    sys::{set_route_configuration, write_packet_with_pi, IFF_PI_PREFIX_LEN},
    tcp::TcpTun,
    udp::UdpTun,
};

mod sys;
mod tcp;
mod udp;

pub struct TunBuilder {
    context: Arc<ServiceContext>,
    balancer: PingBalancer,
    tun_config: TunConfiguration,
    udp_expiry_duration: Option<Duration>,
    udp_capacity: Option<usize>,
    mode: Mode,
}

impl TunBuilder {
    pub fn new(context: Arc<ServiceContext>, balancer: PingBalancer) -> TunBuilder {
        TunBuilder {
            context,
            balancer,
            tun_config: TunConfiguration::default(),
            udp_expiry_duration: None,
            udp_capacity: None,
            mode: Mode::TcpOnly,
        }
    }

    pub fn address(mut self, addr: IpNet) -> TunBuilder {
        self.tun_config.address(addr.addr()).netmask(addr.netmask());
        self
    }

    pub fn name(mut self, name: &str) -> TunBuilder {
        self.tun_config.name(name);
        self
    }

    #[cfg(unix)]
    pub fn file_descriptor(mut self, fd: RawFd) -> TunBuilder {
        self.tun_config.raw_fd(fd);
        self
    }

    pub fn udp_expiry_duration(mut self, udp_expiry_duration: Duration) -> TunBuilder {
        self.udp_expiry_duration = Some(udp_expiry_duration);
        self
    }

    pub fn udp_capacity(mut self, udp_capacity: usize) -> TunBuilder {
        self.udp_capacity = Some(udp_capacity);
        self
    }

    pub fn mode(mut self, mode: Mode) -> TunBuilder {
        self.mode = mode;
        self
    }

    pub async fn build(mut self) -> io::Result<Tun> {
        self.tun_config.layer(Layer::L3).up();

        #[cfg(any(target_os = "linux"))]
        self.tun_config.platform(|tun_config| {
            // IFF_NO_PI preventing excessive buffer reallocating
            tun_config.packet_information(false);
        });

        let device = match tun::create_as_async(&self.tun_config) {
            Ok(d) => d,
            Err(TunError::Io(err)) => return Err(err),
            Err(err) => return Err(io::Error::new(ErrorKind::Other, err)),
        };

        let tun_address = match device.get_ref().address() {
            Ok(t) => t,
            Err(err) => {
                error!(
                    "tun device doesn't have address, error: {}, set it by tun_interface_address",
                    err
                );
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let tun_netmask = match device.get_ref().netmask() {
            Ok(m) => m,
            Err(err) => {
                error!(
                    "tun device doesn't have netmask, error: {}, set it by tun_interface_address",
                    err
                );
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let tun_name = device.get_ref().name();

        trace!("tun {}, address: {}, netmask: {}", tun_name, tun_address, tun_netmask);

        if let Err(err) = set_route_configuration(device.get_ref()).await {
            warn!(
                "failed to set system route for {}, consider set it manually, error: {}",
                tun_name, err
            );
        }

        let tun_netmask_u32: u32 = tun_netmask.into();

        let tun_network = Ipv4Net::new(tun_address, tun_netmask_u32.leading_ones() as u8).expect("Ipv4Net::new");

        Ok(Tun {
            device,
            tcp: if self.mode.enable_tcp() {
                Some(TcpTun::new(self.context.clone(), tun_network.into(), self.balancer.clone()).await?)
            } else {
                None
            },
            udp: if self.mode.enable_udp() {
                Some(UdpTun::new(
                    self.context,
                    self.balancer,
                    self.udp_expiry_duration,
                    self.udp_capacity,
                ))
            } else {
                None
            },
            mode: self.mode,
        })
    }
}

pub struct Tun {
    device: AsyncDevice,
    tcp: Option<TcpTun>,
    udp: Option<UdpTun>,
    mode: Mode,
}

impl Tun {
    pub async fn run(mut self) -> io::Result<()> {
        let mtu = self.device.get_ref().mtu().expect("mtu");
        assert!(mtu > 0 && mtu as usize > IFF_PI_PREFIX_LEN);

        info!(
            "shadowsocks tun device {}, address {}, netmask {}, mtu {}, mode {}",
            self.device.get_ref().name(),
            self.device.get_ref().address().expect("address"),
            self.device.get_ref().netmask().expect("netmask"),
            mtu,
            self.mode,
        );

        let mut packet_buffer = vec![0u8; mtu as usize + IFF_PI_PREFIX_LEN].into_boxed_slice();

        loop {
            #[inline(always)]
            async fn udp_recv_packet(udp: &mut Option<UdpTun>) -> BytesMut {
                match *udp {
                    Some(ref mut udp) => udp.recv_packet().await,
                    None => future::pending().await,
                }
            }

            tokio::select! {
                // tun device
                n = self.device.read(&mut packet_buffer) => {
                    let n = n?;

                    if n <= IFF_PI_PREFIX_LEN {
                        error!(
                            "[TUN] packet too short, packet: {:?}",
                            ByteStr::new(&packet_buffer[..n])
                        );
                        continue;
                    }

                    let packet = &mut packet_buffer[IFF_PI_PREFIX_LEN..n];
                    trace!("[TUN] received IP packet {:?}", ByteStr::new(packet));

                    if self.handle_packet(packet).await? {
                        self.device.write_all(&packet_buffer[..n]).await?;
                    }
                }

                // channel sent back
                packet = udp_recv_packet(&mut self.udp) => {
                    if let Err(err) = write_packet_with_pi(&mut self.device, &packet).await {
                        error!("failed to set packet information, error: {}, {:?}", err, ByteStr::new(&packet));
                    }
                }
            }
        }
    }

    async fn handle_packet(&mut self, packet: &mut [u8]) -> io::Result<bool> {
        let mut ph = match PacketHeaders::from_ip_slice(packet) {
            Ok(ph) => ph,
            Err(ReadError::IoError(err)) => return Err(err),
            Err(err) => {
                error!("invalid IP packet, error: {:?}, {:?}", err, ByteStr::new(packet));
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let payload_len = ph.payload.len();

        let mut ip_header = match ph.ip {
            Some(ref mut i) => i,
            None => {
                error!("unrecognized ethernet packet {:?}", ph);
                return Err(io::Error::new(ErrorKind::Other, "unrecognized ethernet packet"));
            }
        };

        let (src_ip, dst_ip) = match *ip_header {
            IpHeader::Version4(ref v4, ..) => (Ipv4Addr::from(v4.source).into(), Ipv4Addr::from(v4.destination).into()),
            IpHeader::Version6(ref v6, ..) => (Ipv6Addr::from(v6.source).into(), Ipv6Addr::from(v6.destination).into()),
        };

        match ph.transport {
            Some(TransportHeader::Tcp(ref mut tcp_header)) => {
                let tcp = match self.tcp {
                    Some(ref mut tcp) => tcp,
                    None => return Ok(false),
                };

                let src_addr = SocketAddr::new(src_ip, tcp_header.source_port);
                let dst_addr = SocketAddr::new(dst_ip, tcp_header.destination_port);

                let (mod_src_addr, mod_dst_addr) = match tcp.handle_packet(src_addr, dst_addr, tcp_header).await {
                    Ok(Some(a)) => a,
                    Ok(None) => return Ok(false),
                    Err(err) => {
                        error!("handle TCP/IP packet failed, error: {}", err);
                        return Ok(false);
                    }
                };

                // Replaces IP_HEADER, TRANSPORT_HEADER directly into packet
                match (mod_src_addr, &mut ip_header) {
                    (SocketAddr::V4(v4addr), IpHeader::Version4(v4ip, ..)) => v4ip.source = v4addr.ip().octets(),
                    (SocketAddr::V6(v6addr), IpHeader::Version6(v6ip, ..)) => v6ip.source = v6addr.ip().octets(),
                    _ => unreachable!("modified saddr not match"),
                }
                tcp_header.source_port = mod_src_addr.port();
                match (mod_dst_addr, &mut ip_header) {
                    (SocketAddr::V4(v4addr), IpHeader::Version4(v4ip, ..)) => v4ip.destination = v4addr.ip().octets(),
                    (SocketAddr::V6(v6addr), IpHeader::Version6(v6ip, ..)) => v6ip.destination = v6addr.ip().octets(),
                    _ => unreachable!("modified daddr not match"),
                }
                tcp_header.destination_port = mod_dst_addr.port();
                match ip_header {
                    IpHeader::Version4(v4, ..) => {
                        tcp_header.checksum = tcp_header
                            .calc_checksum_ipv4(v4, ph.payload)
                            .expect("calc_checksum_ipv4")
                    }
                    IpHeader::Version6(v6, ..) => {
                        tcp_header.checksum = tcp_header
                            .calc_checksum_ipv6(v6, ph.payload)
                            .expect("calc_checksum_ipv6")
                    }
                }

                let (headers, _) = packet.split_at_mut(packet.len() - payload_len);
                let mut cursor = Cursor::new(headers);

                ip_header.write(&mut cursor).expect("ip_header.write");
                tcp_header.write(&mut cursor).expect("tcp_header.write");

                Ok(true)
            }
            Some(TransportHeader::Udp(ref udp_header)) => {
                // UDP proxies directly
                let udp = match self.udp {
                    Some(ref mut udp) => udp,
                    None => return Ok(false),
                };

                let src_addr = SocketAddr::new(src_ip, udp_header.source_port);
                let dst_addr = SocketAddr::new(dst_ip, udp_header.destination_port);

                if let Err(err) = udp.handle_packet(src_addr, dst_addr, ph.payload).await {
                    error!("handle UDP/IP packet failed, error: {}", err);
                }

                Ok(false)
            }
            None => {
                trace!("no transport layer in ethernet packet {:?}", ph);
                Ok(false)
            }
        }
    }
}
<commit_msg>log saddr and daddr if type unmatch<commit_after>//! Shadowsocks Local server serving on a Tun interface

#[cfg(unix)]
use std::os::unix::io::RawFd;
use std::{
    io::{self, Cursor, ErrorKind},
    net::{Ipv4Addr, Ipv6Addr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use byte_string::ByteStr;
use bytes::BytesMut;
use etherparse::{IpHeader, PacketHeaders, ReadError, TransportHeader};
use futures::future;
use ipnet::{IpNet, Ipv4Net};
use log::{error, info, trace, warn};
use shadowsocks::config::Mode;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tun::{AsyncDevice, Configuration as TunConfiguration, Device, Error as TunError, Layer};

use crate::local::{context::ServiceContext, loadbalancing::PingBalancer};

use self::{
    sys::{set_route_configuration, write_packet_with_pi, IFF_PI_PREFIX_LEN},
    tcp::TcpTun,
    udp::UdpTun,
};

mod sys;
mod tcp;
mod udp;

pub struct TunBuilder {
    context: Arc<ServiceContext>,
    balancer: PingBalancer,
    tun_config: TunConfiguration,
    udp_expiry_duration: Option<Duration>,
    udp_capacity: Option<usize>,
    mode: Mode,
}

impl TunBuilder {
    pub fn new(context: Arc<ServiceContext>, balancer: PingBalancer) -> TunBuilder {
        TunBuilder {
            context,
            balancer,
            tun_config: TunConfiguration::default(),
            udp_expiry_duration: None,
            udp_capacity: None,
            mode: Mode::TcpOnly,
        }
    }

    pub fn address(mut self, addr: IpNet) -> TunBuilder {
        self.tun_config.address(addr.addr()).netmask(addr.netmask());
        self
    }

    pub fn name(mut self, name: &str) -> TunBuilder {
        self.tun_config.name(name);
        self
    }

    #[cfg(unix)]
    pub fn file_descriptor(mut self, fd: RawFd) -> TunBuilder {
        self.tun_config.raw_fd(fd);
        self
    }

    pub fn udp_expiry_duration(mut self, udp_expiry_duration: Duration) -> TunBuilder {
        self.udp_expiry_duration = Some(udp_expiry_duration);
        self
    }

    pub fn udp_capacity(mut self, udp_capacity: usize) -> TunBuilder {
        self.udp_capacity = Some(udp_capacity);
        self
    }

    pub fn mode(mut self, mode: Mode) -> TunBuilder {
        self.mode = mode;
        self
    }

    pub async fn build(mut self) -> io::Result<Tun> {
        self.tun_config.layer(Layer::L3).up();

        #[cfg(any(target_os = "linux"))]
        self.tun_config.platform(|tun_config| {
            // IFF_NO_PI preventing excessive buffer reallocating
            tun_config.packet_information(false);
        });

        let device = match tun::create_as_async(&self.tun_config) {
            Ok(d) => d,
            Err(TunError::Io(err)) => return Err(err),
            Err(err) => return Err(io::Error::new(ErrorKind::Other, err)),
        };

        let tun_address = match device.get_ref().address() {
            Ok(t) => t,
            Err(err) => {
                error!(
                    "tun device doesn't have address, error: {}, set it by tun_interface_address",
                    err
                );
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let tun_netmask = match device.get_ref().netmask() {
            Ok(m) => m,
            Err(err) => {
                error!(
                    "tun device doesn't have netmask, error: {}, set it by tun_interface_address",
                    err
                );
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let tun_name = device.get_ref().name();

        trace!("tun {}, address: {}, netmask: {}", tun_name, tun_address, tun_netmask);

        if let Err(err) = set_route_configuration(device.get_ref()).await {
            warn!(
                "failed to set system route for {}, consider set it manually, error: {}",
                tun_name, err
            );
        }

        let tun_netmask_u32: u32 = tun_netmask.into();

        let tun_network = Ipv4Net::new(tun_address, tun_netmask_u32.leading_ones() as u8).expect("Ipv4Net::new");

        Ok(Tun {
            device,
            tcp: if self.mode.enable_tcp() {
                Some(TcpTun::new(self.context.clone(), tun_network.into(), self.balancer.clone()).await?)
            } else {
                None
            },
            udp: if self.mode.enable_udp() {
                Some(UdpTun::new(
                    self.context,
                    self.balancer,
                    self.udp_expiry_duration,
                    self.udp_capacity,
                ))
            } else {
                None
            },
            mode: self.mode,
        })
    }
}

pub struct Tun {
    device: AsyncDevice,
    tcp: Option<TcpTun>,
    udp: Option<UdpTun>,
    mode: Mode,
}

impl Tun {
    pub async fn run(mut self) -> io::Result<()> {
        let mtu = self.device.get_ref().mtu().expect("mtu");
        assert!(mtu > 0 && mtu as usize > IFF_PI_PREFIX_LEN);

        info!(
            "shadowsocks tun device {}, address {}, netmask {}, mtu {}, mode {}",
            self.device.get_ref().name(),
            self.device.get_ref().address().expect("address"),
            self.device.get_ref().netmask().expect("netmask"),
            mtu,
            self.mode,
        );

        let mut packet_buffer = vec![0u8; mtu as usize + IFF_PI_PREFIX_LEN].into_boxed_slice();

        loop {
            #[inline(always)]
            async fn udp_recv_packet(udp: &mut Option<UdpTun>) -> BytesMut {
                match *udp {
                    Some(ref mut udp) => udp.recv_packet().await,
                    None => future::pending().await,
                }
            }

            tokio::select! {
                // tun device
                n = self.device.read(&mut packet_buffer) => {
                    let n = n?;

                    if n <= IFF_PI_PREFIX_LEN {
                        error!(
                            "[TUN] packet too short, packet: {:?}",
                            ByteStr::new(&packet_buffer[..n])
                        );
                        continue;
                    }

                    let packet = &mut packet_buffer[IFF_PI_PREFIX_LEN..n];
                    trace!("[TUN] received IP packet {:?}", ByteStr::new(packet));

                    if self.handle_packet(packet).await? {
                        self.device.write_all(&packet_buffer[..n]).await?;
                    }
                }

                // channel sent back
                packet = udp_recv_packet(&mut self.udp) => {
                    if let Err(err) = write_packet_with_pi(&mut self.device, &packet).await {
                        error!("failed to set packet information, error: {}, {:?}", err, ByteStr::new(&packet));
                    }
                }
            }
        }
    }

    async fn handle_packet(&mut self, packet: &mut [u8]) -> io::Result<bool> {
        let mut ph = match PacketHeaders::from_ip_slice(packet) {
            Ok(ph) => ph,
            Err(ReadError::IoError(err)) => return Err(err),
            Err(err) => {
                error!("invalid IP packet, error: {:?}, {:?}", err, ByteStr::new(packet));
                return Err(io::Error::new(ErrorKind::Other, err));
            }
        };

        let payload_len = ph.payload.len();

        let mut ip_header = match ph.ip {
            Some(ref mut i) => i,
            None => {
                error!("unrecognized ethernet packet {:?}", ph);
                return Err(io::Error::new(ErrorKind::Other, "unrecognized ethernet packet"));
            }
        };

        let (src_ip, dst_ip) = match *ip_header {
            IpHeader::Version4(ref v4, ..) => (Ipv4Addr::from(v4.source).into(), Ipv4Addr::from(v4.destination).into()),
            IpHeader::Version6(ref v6, ..) => (Ipv6Addr::from(v6.source).into(), Ipv6Addr::from(v6.destination).into()),
        };

        match ph.transport {
            Some(TransportHeader::Tcp(ref mut tcp_header)) => {
                let tcp = match self.tcp {
                    Some(ref mut tcp) => tcp,
                    None => return Ok(false),
                };

                let src_addr = SocketAddr::new(src_ip, tcp_header.source_port);
                let dst_addr = SocketAddr::new(dst_ip, tcp_header.destination_port);

                let (mod_src_addr, mod_dst_addr) = match tcp.handle_packet(src_addr, dst_addr, tcp_header).await {
                    Ok(Some(a)) => a,
                    Ok(None) => return Ok(false),
                    Err(err) => {
                        error!("handle TCP/IP packet failed, error: {}", err);
                        return Ok(false);
                    }
                };

                // Replaces IP_HEADER, TRANSPORT_HEADER directly into packet
                match (mod_src_addr, &mut ip_header) {
                    (SocketAddr::V4(v4addr), IpHeader::Version4(v4ip, ..)) => v4ip.source = v4addr.ip().octets(),
                    (SocketAddr::V6(v6addr), IpHeader::Version6(v6ip, ..)) => v6ip.source = v6addr.ip().octets(),
                    _ => {
                        unreachable!(
                            "modified TCP saddr not match, addr: {}, header: {:?}",
                            mod_src_addr, ip_header
                        );
                    }
                }
                tcp_header.source_port = mod_src_addr.port();
                match (mod_dst_addr, &mut ip_header) {
                    (SocketAddr::V4(v4addr), IpHeader::Version4(v4ip, ..)) => v4ip.destination = v4addr.ip().octets(),
                    (SocketAddr::V6(v6addr), IpHeader::Version6(v6ip, ..)) => v6ip.destination = v6addr.ip().octets(),
                    _ => {
                        unreachable!(
                            "modified TCP daddr not match, addr: {}, header: {:?}",
                            mod_dst_addr, ip_header
                        );
                    }
                }
                tcp_header.destination_port = mod_dst_addr.port();
                match ip_header {
                    IpHeader::Version4(v4, ..) => {
                        tcp_header.checksum = tcp_header
                            .calc_checksum_ipv4(v4, ph.payload)
                            .expect("calc_checksum_ipv4")
                    }
                    IpHeader::Version6(v6, ..) => {
                        tcp_header.checksum = tcp_header
                            .calc_checksum_ipv6(v6, ph.payload)
                            .expect("calc_checksum_ipv6")
                    }
                }

                let (headers, _) = packet.split_at_mut(packet.len() - payload_len);
                let mut cursor = Cursor::new(headers);

                ip_header.write(&mut cursor).expect("ip_header.write");
                tcp_header.write(&mut cursor).expect("tcp_header.write");

                Ok(true)
            }
            Some(TransportHeader::Udp(ref udp_header)) => {
                // UDP proxies directly
                let udp = match self.udp {
                    Some(ref mut udp) => udp,
                    None => return Ok(false),
                };

                let src_addr = SocketAddr::new(src_ip, udp_header.source_port);
                let dst_addr = SocketAddr::new(dst_ip, udp_header.destination_port);

                if let Err(err) = udp.handle_packet(src_addr, dst_addr, ph.payload).await {
                    error!("handle UDP/IP packet failed, error: {}", err);
                }

                Ok(false)
            }
            None => {
                trace!("no transport layer in ethernet packet {:?}", ph);
                Ok(false)
            }
        }
    }
}
<|endoftext|>


<commit_before>//! A parser and representation of regular expressions.

use std::fmt::{Debug, Display, Formatter, Error};
use std::str::CharIndices;

#[cfg(test)]
mod test;

pub struct Regex {
    pub alternatives: Vec<Alternative>
}

pub struct Alternative {
    pub elems: Vec<Elem>
}

pub enum Elem {
    Any,
    Test(Test),
    Group(Regex),
    NotGroup(Regex),
    Repeat(RepeatOp, Box<Elem>),
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Test {
    Char(char), // some specific character
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum RepeatOp {
    Plus,     // R+     1 .. infinity
    Question, // R?     0 .. 1
    Star,     // R*     0 .. infinity
}

#[derive(Debug)]
pub struct RegexError {
    pub message: String,
    pub offset: usize,
}

macro_rules! return_err {
    ($offset: expr, $($args:expr),+) => {
        return Err(RegexError {
            message: format!($($args),+),
            offset: $offset
        });
    }
}

/// Convert a string literal into a parsed regular expression.
pub fn parse_literal(s: &str) -> Regex {
    Regex {
        alternatives: vec![
            Alternative {
                elems: s.chars().map(|c| Elem::Test(Test::Char(c))).collect()
            }]
    }
}

/// Parse a regular expression like `a+` etc.
pub fn parse_regex(s: &str) -> Result<Regex, RegexError> {
    let mut parser = RegexParser::new(s.char_indices(), s.len());
    let regex = try!(parser.regex());

    // we should have reached the end of the string:
    match parser.lookahead {
        Some((index, c)) => return_err!(index, "unexpected '{}'", c),
        None => Ok(regex),
    }
}

struct RegexParser<'str> {
    lookahead: Option<(usize, char)>,
    length: usize, // length of string in total
    chars: CharIndices<'str>,
}

// little recursive descent parser
impl<'str> RegexParser<'str> {
    fn new(mut chars: CharIndices<'str>, length: usize) -> RegexParser<'str> {
        let lookahead = chars.next();
        RegexParser { lookahead: lookahead, chars: chars, length: length }
    }

    fn bump(&mut self) {
        self.lookahead = self.chars.next();
    }

    fn expect(&mut self, c: char) -> Result<(), RegexError> {
        match self.lookahead {
            Some((_, d)) if c == d => Ok(self.bump()),
            Some((index, d)) => return_err!(index, "expected '{}', but found '{}'", c, d),
            _ => return_err!(self.length, "expected '{}', but regex ended", c)
        }
    }

    fn regex(&mut self) -> Result<Regex, RegexError> {
        let mut alternatives = vec![];

        loop {
            alternatives.push(try!(self.alternative()));

            match self.lookahead {
                Some((_, '|')) => { continue; }
                _ => { break; }
            }
        }

        Ok(Regex { alternatives: alternatives })
    }

    fn alternative(&mut self) -> Result<Alternative, RegexError> {
        let mut elems = vec![];
        while let Some((index, c)) = self.lookahead {
            match c {
                '\\' => { elems.push(try!(self.escape(index))); }
                '*'  => { try!(self.repeat(&mut elems, index, RepeatOp::Star)); }
                '+'  => { try!(self.repeat(&mut elems, index, RepeatOp::Plus)); }
                '?'  => { try!(self.repeat(&mut elems, index, RepeatOp::Question)); }
                '('  => { elems.push(try!(self.group())); }
                ')'  => { break; }
                '['  => { elems.push(try!(self.range(index))); }
                ']'  => { break; }
                '|'  => { break; }
                '.'  => { self.bump(); elems.push(Elem::Any); }
                _    => { self.bump(); elems.push(Elem::Test(Test::Char(c))); }
            }
        }
        Ok(Alternative { elems: elems })
    }

    fn escape(&mut self, index: usize) -> Result<Elem, RegexError> {
        self.bump(); // consume the '\\'
        match self.lookahead {
            None => return_err!(index, "escape at end of str"),
            Some((_, '!')) => {
                // (*) afaik, \! is not particularly meaningful or
                // useful in ordinary regular expressions, and there
                // doesn't seem to be any decent way to express
                // something like [^abc], so overload it

                self.bump();
                try!(self.expect('('));
                let regex = try!(self.regex());
                try!(self.expect(')'));
                Ok(Elem::NotGroup(regex))
            }
            Some((_, c)) => {
                self.bump();
                Ok(Elem::Test(Test::Char(c))) // FIXME there are other escapes, like \s
            }
        }
    }

    fn repeat(&mut self, elems: &mut Vec<Elem>, index: usize, op: RepeatOp) -> Result<(), RegexError> {
        self.bump(); // consume the `*`, `+`, `?`, etc
        match elems.pop() {
            Some(e) => Ok(elems.push(Elem::Repeat(op, Box::new(e)))),
            None => return_err!(index, "modifier `{}` has nothing to modify", op),
        }
    }

    fn group(&mut self) -> Result<Elem, RegexError> {
        self.bump(); // consume the `(`
        let regex = try!(self.regex());
        try!(self.expect(')'));
        Ok(Elem::Group(regex))
    }

    fn range(&mut self, index: usize) -> Result<Elem, RegexError> {
        // FIXME: This only supports the basic stuff, not the fancy
        // things like [:alpha:] or [.ch.], whatever the heck THAT is!

        self.bump(); // consume the `[`

        let negative = match self.lookahead {
            Some((_, '^')) => { self.bump(); true }
            _ => { false }
        };

        let mut chars = vec![];

        // as a special case, a `]` or `-` first in the list is treated as itself
        match self.lookahead {
            Some((_, ']')) => { self.bump(); chars.push(']'); }
            _ => { }
        }

        while let Some((index, c)) = self.lookahead {
            match c {
                '-' => {
                    self.bump();
                    try!(self.range_dash(index, &mut chars));
                }

                ']' => {
                    self.bump();

                    // "desugar" the range into `|` notation, so that
                    // [abc] becomes (a|b|c)
                    let regex = Regex {
                        alternatives: chars.into_iter()
                                           .map(|c| Alternative {
                                               elems: vec![Elem::Test(Test::Char(c))]
                                           })
                                           .collect()
                    };

                    return if negative {
                        Ok(Elem::NotGroup(regex))
                    } else {
                        Ok(Elem::Group(regex))
                    };
                }

                _ => {
                    self.bump();
                    chars.push(c);
                }
            }
        }

        return_err!(index, "unterminated `[`");
    }

    fn range_dash(&mut self, index: usize, chars: &mut Vec<char>) -> Result<(), RegexError> {
        // as a special case, a `-` at the beginning of the range is treated as itself
        if chars.is_empty() {
            chars.push('-');
            return Ok(());
        }

        // similarly, a - as the last thing in the range is itself
        let end_char = match self.lookahead {
            None | Some((_, ']')) => {
                chars.push('-');
                return Ok(());
            }

            Some((_, d)) => {
                self.bump();
                d
            }
        };

        // otherwise, we will expand the last character we've seen
        let start_char = *chars.last().unwrap();
        let start_index = start_char as u32;
        if start_index >= 128 {
            return_err!(index, "dash notation only supported for ASCII");
        }

        let end_index = end_char as u32;
        if end_index >= 128 {
            return_err!(index, "dash notation only supported for ASCII");
        }

        if start_index > end_index {
            return_err!(index, "start of range is higher than the end of range");
        }

        for c in (start_index as u8)+1 .. (end_index as u8)+1 {
            chars.push(c as char);
        }

        Ok(())
    }
}

impl Debug for RepeatOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        Display::fmt(self, fmt)
    }
}

impl Display for RepeatOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            RepeatOp::Plus => write!(fmt, "+"),
            RepeatOp::Star => write!(fmt, "*"),
            RepeatOp::Question => write!(fmt, "?"),
        }
    }
}

impl Debug for Regex {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        if !self.alternatives.is_empty() {
            try!(write!(fmt, "{:?}", self.alternatives[0]));
            for alt in &self.alternatives[1..] { try!(write!(fmt, "|{:?}", alt)); }
        } else {
            try!(write!(fmt, "()"));
        }
        Ok(())
    }
}

impl Debug for Alternative {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        for elem in &self.elems {
            try!(write!(fmt, "{:?}", elem));
        }
        Ok(())
    }
}

impl Debug for Elem {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Elem::Any => write!(fmt, "."),
            Elem::Test(Test::Char(c)) => {
                if ".[]()?+*!".contains(c) {
                    write!(fmt, "\\{}", c)
                } else {
                    write!(fmt, "{}", c)
                }
            }
            Elem::Group(ref regex) => write!(fmt, "({:?})", regex),
            Elem::NotGroup(ref regex) => write!(fmt, "\\!({:?})", regex), // see (*) above
            Elem::Repeat(op, ref elem) => write!(fmt, "{:?}{}", elem, op),
        }
    }
}

impl Test {
    pub fn meets(self, t: Test) -> bool {
        use self::Test::*;
        match (self, t) {
            (Char(c), Char(d)) => c == d,
        }
    }
}
<commit_msg>Fix #41 by adding a missing self.bump()<commit_after>//! A parser and representation of regular expressions.

use std::fmt::{Debug, Display, Formatter, Error};
use std::str::CharIndices;

#[cfg(test)]
mod test;

pub struct Regex {
    pub alternatives: Vec<Alternative>
}

pub struct Alternative {
    pub elems: Vec<Elem>
}

pub enum Elem {
    Any,
    Test(Test),
    Group(Regex),
    NotGroup(Regex),
    Repeat(RepeatOp, Box<Elem>),
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Test {
    Char(char), // some specific character
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum RepeatOp {
    Plus,     // R+     1 .. infinity
    Question, // R?     0 .. 1
    Star,     // R*     0 .. infinity
}

#[derive(Debug)]
pub struct RegexError {
    pub message: String,
    pub offset: usize,
}

macro_rules! return_err {
    ($offset: expr, $($args:expr),+) => {
        return Err(RegexError {
            message: format!($($args),+),
            offset: $offset
        });
    }
}

/// Convert a string literal into a parsed regular expression.
pub fn parse_literal(s: &str) -> Regex {
    Regex {
        alternatives: vec![
            Alternative {
                elems: s.chars().map(|c| Elem::Test(Test::Char(c))).collect()
            }]
    }
}

/// Parse a regular expression like `a+` etc.
pub fn parse_regex(s: &str) -> Result<Regex, RegexError> {
    let mut parser = RegexParser::new(s.char_indices(), s.len());
    let regex = try!(parser.regex());

    // we should have reached the end of the string:
    match parser.lookahead {
        Some((index, c)) => return_err!(index, "unexpected '{}'", c),
        None => Ok(regex),
    }
}

struct RegexParser<'str> {
    lookahead: Option<(usize, char)>,
    length: usize, // length of string in total
    chars: CharIndices<'str>,
}

// little recursive descent parser
impl<'str> RegexParser<'str> {
    fn new(mut chars: CharIndices<'str>, length: usize) -> RegexParser<'str> {
        let lookahead = chars.next();
        RegexParser { lookahead: lookahead, chars: chars, length: length }
    }

    fn bump(&mut self) {
        self.lookahead = self.chars.next();
    }

    fn expect(&mut self, c: char) -> Result<(), RegexError> {
        match self.lookahead {
            Some((_, d)) if c == d => Ok(self.bump()),
            Some((index, d)) => return_err!(index, "expected '{}', but found '{}'", c, d),
            _ => return_err!(self.length, "expected '{}', but regex ended", c)
        }
    }

    fn regex(&mut self) -> Result<Regex, RegexError> {
        let mut alternatives = vec![];

        loop {
            alternatives.push(try!(self.alternative()));

            match self.lookahead {
                Some((_, '|')) => { self.bump(); continue; }
                _ => { break; }
            }
        }

        Ok(Regex { alternatives: alternatives })
    }

    fn alternative(&mut self) -> Result<Alternative, RegexError> {
        let mut elems = vec![];
        while let Some((index, c)) = self.lookahead {
            match c {
                '\\' => { elems.push(try!(self.escape(index))); }
                '*'  => { try!(self.repeat(&mut elems, index, RepeatOp::Star)); }
                '+'  => { try!(self.repeat(&mut elems, index, RepeatOp::Plus)); }
                '?'  => { try!(self.repeat(&mut elems, index, RepeatOp::Question)); }
                '('  => { elems.push(try!(self.group())); }
                ')'  => { break; }
                '['  => { elems.push(try!(self.range(index))); }
                ']'  => { break; }
                '|'  => { break; }
                '.'  => { self.bump(); elems.push(Elem::Any); }
                _    => { self.bump(); elems.push(Elem::Test(Test::Char(c))); }
            }
        }
        Ok(Alternative { elems: elems })
    }

    fn escape(&mut self, index: usize) -> Result<Elem, RegexError> {
        self.bump(); // consume the '\\'
        match self.lookahead {
            None => return_err!(index, "escape at end of str"),
            Some((_, '!')) => {
                // (*) afaik, \! is not particularly meaningful or
                // useful in ordinary regular expressions, and there
                // doesn't seem to be any decent way to express
                // something like [^abc], so overload it

                self.bump();
                try!(self.expect('('));
                let regex = try!(self.regex());
                try!(self.expect(')'));
                Ok(Elem::NotGroup(regex))
            }
            Some((_, c)) => {
                self.bump();
                Ok(Elem::Test(Test::Char(c))) // FIXME there are other escapes, like \s
            }
        }
    }

    fn repeat(&mut self, elems: &mut Vec<Elem>, index: usize, op: RepeatOp) -> Result<(), RegexError> {
        self.bump(); // consume the `*`, `+`, `?`, etc
        match elems.pop() {
            Some(e) => Ok(elems.push(Elem::Repeat(op, Box::new(e)))),
            None => return_err!(index, "modifier `{}` has nothing to modify", op),
        }
    }

    fn group(&mut self) -> Result<Elem, RegexError> {
        self.bump(); // consume the `(`
        let regex = try!(self.regex());
        try!(self.expect(')'));
        Ok(Elem::Group(regex))
    }

    fn range(&mut self, index: usize) -> Result<Elem, RegexError> {
        // FIXME: This only supports the basic stuff, not the fancy
        // things like [:alpha:] or [.ch.], whatever the heck THAT is!

        self.bump(); // consume the `[`

        let negative = match self.lookahead {
            Some((_, '^')) => { self.bump(); true }
            _ => { false }
        };

        let mut chars = vec![];

        // as a special case, a `]` or `-` first in the list is treated as itself
        match self.lookahead {
            Some((_, ']')) => { self.bump(); chars.push(']'); }
            _ => { }
        }

        while let Some((index, c)) = self.lookahead {
            match c {
                '-' => {
                    self.bump();
                    try!(self.range_dash(index, &mut chars));
                }

                ']' => {
                    self.bump();

                    // "desugar" the range into `|` notation, so that
                    // [abc] becomes (a|b|c)
                    let regex = Regex {
                        alternatives: chars.into_iter()
                                           .map(|c| Alternative {
                                               elems: vec![Elem::Test(Test::Char(c))]
                                           })
                                           .collect()
                    };

                    return if negative {
                        Ok(Elem::NotGroup(regex))
                    } else {
                        Ok(Elem::Group(regex))
                    };
                }

                _ => {
                    self.bump();
                    chars.push(c);
                }
            }
        }

        return_err!(index, "unterminated `[`");
    }

    fn range_dash(&mut self, index: usize, chars: &mut Vec<char>) -> Result<(), RegexError> {
        // as a special case, a `-` at the beginning of the range is treated as itself
        if chars.is_empty() {
            chars.push('-');
            return Ok(());
        }

        // similarly, a - as the last thing in the range is itself
        let end_char = match self.lookahead {
            None | Some((_, ']')) => {
                chars.push('-');
                return Ok(());
            }

            Some((_, d)) => {
                self.bump();
                d
            }
        };

        // otherwise, we will expand the last character we've seen
        let start_char = *chars.last().unwrap();
        let start_index = start_char as u32;
        if start_index >= 128 {
            return_err!(index, "dash notation only supported for ASCII");
        }

        let end_index = end_char as u32;
        if end_index >= 128 {
            return_err!(index, "dash notation only supported for ASCII");
        }

        if start_index > end_index {
            return_err!(index, "start of range is higher than the end of range");
        }

        for c in (start_index as u8)+1 .. (end_index as u8)+1 {
            chars.push(c as char);
        }

        Ok(())
    }
}

impl Debug for RepeatOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        Display::fmt(self, fmt)
    }
}

impl Display for RepeatOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            RepeatOp::Plus => write!(fmt, "+"),
            RepeatOp::Star => write!(fmt, "*"),
            RepeatOp::Question => write!(fmt, "?"),
        }
    }
}

impl Debug for Regex {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        if !self.alternatives.is_empty() {
            try!(write!(fmt, "{:?}", self.alternatives[0]));
            for alt in &self.alternatives[1..] { try!(write!(fmt, "|{:?}", alt)); }
        } else {
            try!(write!(fmt, "()"));
        }
        Ok(())
    }
}

impl Debug for Alternative {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        for elem in &self.elems {
            try!(write!(fmt, "{:?}", elem));
        }
        Ok(())
    }
}

impl Debug for Elem {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            Elem::Any => write!(fmt, "."),
            Elem::Test(Test::Char(c)) => {
                if ".[]()?+*!".contains(c) {
                    write!(fmt, "\\{}", c)
                } else {
                    write!(fmt, "{}", c)
                }
            }
            Elem::Group(ref regex) => write!(fmt, "({:?})", regex),
            Elem::NotGroup(ref regex) => write!(fmt, "\\!({:?})", regex), // see (*) above
            Elem::Repeat(op, ref elem) => write!(fmt, "{:?}{}", elem, op),
        }
    }
}

impl Test {
    pub fn meets(self, t: Test) -> bool {
        use self::Test::*;
        match (self, t) {
            (Char(c), Char(d)) => c == d,
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::from_str::FromStr;

use super::rfc5322::Rfc5322Parser;
use super::header::FromHeader;

/// Represents an RFC 5322 Address
#[deriving(PartialEq, Eq)]
pub enum Address {
    /// A "regular" email address
    AddressMailbox(Mailbox),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    pub fn mailbox(address: String) -> Address {
        AddressMailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    pub fn mailbox_with_name(name: String, address: String) -> Address {
        AddressMailbox(Mailbox::new_with_name(name, address))
    }
}

impl fmt::Show for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AddressMailbox(ref mbox) => mbox.fmt(fmt),
        }
    }
}

/// Represents an RFC 5322 mailbox
#[deriving(PartialEq, Eq)]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Show for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    fn from_str(s: &str) -> Option<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> Option<Vec<Address>> {
        Some(AddressParser::new(value.as_slice()).parse_address_list())
    }
}

pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    pub fn parse_address_list(&mut self) -> Vec<Address> {
        let mut result = Vec::new();

        while !self.p.eof() {
            match self.parse_mailbox() {
                Some(mailbox) => result.push(AddressMailbox(mailbox)),
                None => {},
            }

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == ',' {
                // Clear the separator
                self.p.consume_char();
            }
        }

        result
    }

    pub fn parse_mailbox(&mut self) -> Option<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        let mut result = self.parse_name_addr();
        if result.is_none() {
            // Revert back to our original position to try to parse an addr-spec
            self.p.pop_position();

            let addr_spec = self.parse_addr_spec();
            if addr_spec.is_some() {
                result = Some(Mailbox::new(addr_spec.unwrap()));
            }
        }

        result
    }

    fn parse_name_addr(&mut self) -> Option<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();
        // Find angle-addr
        if !self.p.eof() && self.p.peek() == '<' {
            self.p.consume_char();
            let addr = self.parse_addr_spec();
            if self.p.consume_char() != '>' {
                // Fail because we should have a closing RANGLE here (to match the opening one)
                None
            } else {
                match (display_name, addr) {
                    (Some(name), Some(addr)) => Some(Mailbox::new_with_name(name, addr)),
                    (None, Some(addr)) => Some(Mailbox::new(addr)),
                    (_, _) => None,
                }
            }
        } else {
            None
        }
    }

    fn parse_addr_spec(&mut self) -> Option<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = self.p.consume_phrase(true);
        if self.p.eof() || self.p.consume_char() != '@' {
            None
        } else {
            let domain = self.parse_domain();
            if local_part.is_some() && domain.is_some() {
                Some(format!("{}@{}", local_part.unwrap(), domain.unwrap()))
            } else {
                None
            }
        }
    }

    fn parse_domain(&mut self) -> Option<String> {
        // TODO: support domain-literal
        self.p.consume_atom(true)
    }
}

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

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = from_str::<Mailbox>("\"Joe Blogs\" <joe@example.org>").unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!(from_str::<Mailbox>("Not an address").is_none());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list(), vec![
            Address::mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }
}
<commit_msg>Add support for address groups.<commit_after>use std::fmt;
use std::from_str::FromStr;

use super::rfc5322::Rfc5322Parser;
use super::header::FromHeader;

/// Represents an RFC 5322 Address
#[deriving(PartialEq, Eq)]
pub enum Address {
    /// A "regular" email address
    AddressMailbox(Mailbox),
    /// A named group of mailboxes
    AddressGroup(String, Vec<Mailbox>),
}

impl Address {
    /// Shortcut function to make a new Mailbox with the given address
    pub fn mailbox(address: String) -> Address {
        AddressMailbox(Mailbox::new(address))
    }

    /// Shortcut function to make a new Mailbox with the address and given-name
    pub fn mailbox_with_name(name: String, address: String) -> Address {
        AddressMailbox(Mailbox::new_with_name(name, address))
    }

    pub fn group(name: String, mailboxes: Vec<Mailbox>) -> Address {
        AddressGroup(name, mailboxes)
    }
}

impl fmt::Show for Address {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AddressMailbox(ref mbox) => mbox.fmt(fmt),
            AddressGroup(ref name, ref mboxes) => {
                let mut mailbox_list = String::new();
                for mbox in mboxes.iter() {
                    if mailbox_list.len() > 0 {
                        // Insert the separator if there's already things in this list
                        mailbox_list.push_str(", ");
                    }
                    mailbox_list.push_str(mbox.to_string().as_slice());
                }
                write!(fmt, "{}: {}", name, mailbox_list)
            }
        }
    }
}

/// Represents an RFC 5322 mailbox
#[deriving(PartialEq, Eq)]
pub struct Mailbox {
    /// The given name for this address
    pub name: Option<String>,
    /// The mailbox address
    pub address: String,
}

impl Mailbox {
    /// Create a new Mailbox without a display name
    pub fn new(address: String) -> Mailbox {
        Mailbox {
            name: None,
            address: address
        }
    }

    /// Create a new Mailbox with a display name
    pub fn new_with_name(name: String, address: String) -> Mailbox {
        Mailbox {
            name: Some(name),
            address: address
        }
    }
}

impl fmt::Show for Mailbox {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.name {
            Some(ref name) => write!(fmt, "\"{}\" <{}>", name, self.address),
            None => write!(fmt, "<{}>", self.address),
        }
    }
}

impl FromStr for Mailbox {
    fn from_str(s: &str) -> Option<Mailbox> {
        AddressParser::new(s).parse_mailbox()
    }
}

impl FromHeader for Vec<Address> {
    fn from_header(value: String) -> Option<Vec<Address>> {
        Some(AddressParser::new(value.as_slice()).parse_address_list())
    }
}

pub struct AddressParser<'s> {
    p: Rfc5322Parser<'s>,
}

impl<'s> AddressParser<'s> {
    pub fn new(s: &str) -> AddressParser {
        AddressParser {
            p: Rfc5322Parser::new(s)
        }
    }

    pub fn parse_address_list(&mut self) -> Vec<Address> {
        let mut result = Vec::new();

        while !self.p.eof() {
            match self.parse_mailbox() {
                Some(mailbox) => result.push(AddressMailbox(mailbox)),
                None => {},
            }

            self.p.consume_linear_whitespace();
            if !self.p.eof() && self.p.peek() == ',' {
                // Clear the separator
                self.p.consume_char();
            }
        }

        result
    }

    pub fn parse_mailbox(&mut self) -> Option<Mailbox> {
        // Push the current position of the parser so we can back out later
        self.p.push_position();
        let mut result = self.parse_name_addr();
        if result.is_none() {
            // Revert back to our original position to try to parse an addr-spec
            self.p.pop_position();

            let addr_spec = self.parse_addr_spec();
            if addr_spec.is_some() {
                result = Some(Mailbox::new(addr_spec.unwrap()));
            }
        }

        result
    }

    fn parse_name_addr(&mut self) -> Option<Mailbox> {
        // Find display-name
        let display_name = self.p.consume_phrase(false);
        self.p.consume_linear_whitespace();
        // Find angle-addr
        if !self.p.eof() && self.p.peek() == '<' {
            self.p.consume_char();
            let addr = self.parse_addr_spec();
            if self.p.consume_char() != '>' {
                // Fail because we should have a closing RANGLE here (to match the opening one)
                None
            } else {
                match (display_name, addr) {
                    (Some(name), Some(addr)) => Some(Mailbox::new_with_name(name, addr)),
                    (None, Some(addr)) => Some(Mailbox::new(addr)),
                    (_, _) => None,
                }
            }
        } else {
            None
        }
    }

    fn parse_addr_spec(&mut self) -> Option<String> {
        // local-part is a phrase, but allows dots in atoms
        let local_part = self.p.consume_phrase(true);
        if self.p.eof() || self.p.consume_char() != '@' {
            None
        } else {
            let domain = self.parse_domain();
            if local_part.is_some() && domain.is_some() {
                Some(format!("{}@{}", local_part.unwrap(), domain.unwrap()))
            } else {
                None
            }
        }
    }

    fn parse_domain(&mut self) -> Option<String> {
        // TODO: support domain-literal
        self.p.consume_atom(true)
    }
}

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

    use super::super::header::Header;

    #[test]
    fn test_address_to_string() {
        let addr = Mailbox::new("foo@example.org".to_string());
        assert_eq!(addr.to_string(), "<foo@example.org>".to_string());

        let name_addr = Mailbox::new_with_name("Joe Blogs".to_string(), "foo@example.org".to_string());
        assert_eq!(name_addr.to_string(), "\"Joe Blogs\" <foo@example.org>".to_string());
    }

    #[test]
    fn test_address_from_string() {
        let addr = from_str::<Mailbox>("\"Joe Blogs\" <joe@example.org>").unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        assert!(from_str::<Mailbox>("Not an address").is_none());
    }

    #[test]
    fn test_address_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>");
        let mut addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name.unwrap(), "Joe Blogs".to_string());
        assert_eq!(addr.address, "joe@example.org".to_string());

        parser = AddressParser::new("joe@example.org");
        addr = parser.parse_mailbox().unwrap();
        assert_eq!(addr.name, None);
        assert_eq!(addr.address, "joe@example.org".to_string());
    }

    #[test]
    fn test_address_list_parsing() {
        let mut parser = AddressParser::new("\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>");
        assert_eq!(parser.parse_address_list(), vec![
            Address::mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_from_header_parsing() {
        let header = Header::new(
            "From:".to_string(),
            "\"Joe Blogs\" <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
        let addresses: Vec<Address> = header.get_value().unwrap();
        assert_eq!(addresses, vec![
            Address::mailbox_with_name("Joe Blogs".to_string(), "joe@example.org".to_string()),
            Address::mailbox_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
    }

    #[test]
    fn test_address_group_to_string() {
        let addr = Address::group("undisclosed recipients".to_string(), vec![]);
        assert_eq!(addr.to_string(), "undisclosed recipients: ".to_string());

        let addr = Address::group("group test".to_string(), vec![
            Mailbox::new("joe@example.org".to_string()),
            Mailbox::new_with_name("John Doe".to_string(), "john@example.org".to_string()),
        ]);
        assert_eq!(addr.to_string(), "group test: <joe@example.org>, \"John Doe\" <john@example.org>".to_string());
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use app::App;
use app::Msg::CreateWindow;
use webview::Msg::PageOpen;
use url::{Url, Position};
use urls::{offset, get_filename};

impl App {
    /// Open the given URL in the web view.
    pub fn open(&self, url: &str) {
        let url = self.transform_url(url);
        self.webview.emit(PageOpen(url));
    }

    /// Open the given URL in a new window.
    pub fn open_in_new_window(&mut self, url: &str) {
        let url = self.transform_url(url);
        self.model.relm.stream().emit(CreateWindow(url));
    }

    /// Open in a new window the url from the system clipboard.
    pub fn win_paste_url(&mut self) {
        if let Some(url) = self.get_url_from_clipboard() {
            self.open_in_new_window(&url);
        }
    }

    /// Go up one directory in url
    pub fn go_parent_directory(&self) {
        let mut parent = String::new();

        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Some(filename) = get_filename(url) {
                if filename.is_empty() {
                    if let Ok(base_url) = Url::parse(url) {
                        parent = base_url.join("../").unwrap().to_string();
                    }
                } else {
                    parent = url[..url.len()-filename.len()].to_string();
                }
            }

            if !parent.is_empty() {
                self.open(&parent);
            }
        }
    }

    /// Go to the root directory or url hostname
    pub fn go_root_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {
                let root = &base_url[..Position::BeforePath];

                if !root.is_empty() {
                    self.open(root);
                }
            }
        }
    }

    pub fn url_increment(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Some(url) = offset(url, 1) {
                self.open(&url);
            }
        }
    }

    pub fn url_decrement(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Some(url) = offset(url, -1) {
                self.open(&url);
            }
        }
    }
}
<commit_msg>added todo<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use app::App;
use app::Msg::CreateWindow;
use webview::Msg::PageOpen;
use url::{Url, Position};
use urls::{offset, get_filename};

impl App {
    /// Open the given URL in the web view.
    pub fn open(&self, url: &str) {
        let url = self.transform_url(url);
        self.webview.emit(PageOpen(url));
    }

    /// Open the given URL in a new window.
    pub fn open_in_new_window(&mut self, url: &str) {
        let url = self.transform_url(url);
        self.model.relm.stream().emit(CreateWindow(url));
    }

    /// Open in a new window the url from the system clipboard.
    pub fn win_paste_url(&mut self) {
        if let Some(url) = self.get_url_from_clipboard() {
            self.open_in_new_window(&url);
        }
    }

    /// Go up one directory in url
    pub fn go_parent_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            let mut parent = String::new();

            /// TODO: Do manually without use of get_filename
            if let Some(filename) = get_filename(url) {
                if filename.is_empty() {
                    if let Ok(base_url) = Url::parse(url) {
                        parent = base_url.join("../").unwrap().to_string();
                    }
                } else {
                    parent = url[..url.len()-filename.len()].to_string();
                }
            }

            if !parent.is_empty() {
                self.open(&parent);
            }
        }
    }

    /// Go to the root directory or url hostname
    pub fn go_root_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {
                let root = &base_url[..Position::BeforePath];

                if !root.is_empty() {
                    self.open(root);
                }
            }
        }
    }

    pub fn url_increment(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Some(url) = offset(url, 1) {
                self.open(&url);
            }
        }
    }

    pub fn url_decrement(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Some(url) = offset(url, -1) {
                self.open(&url);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Creation of ar archives like for the lib and staticlib crate type

use std::collections::BTreeMap;
use std::fs::File;
use std::path::{Path, PathBuf};

use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
use rustc_codegen_ssa::METADATA_FILENAME;
use rustc_session::Session;

use object::{Object, ObjectSymbol, SymbolKind};

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive { archive_index: usize, entry_index: usize },
    File(PathBuf),
}

pub(crate) struct ArArchiveBuilder<'a> {
    sess: &'a Session,
    dst: PathBuf,
    lib_search_paths: Vec<PathBuf>,
    use_gnu_style_archive: bool,
    no_builtin_ranlib: bool,

    src_archives: Vec<(PathBuf, ar::Archive<File>)>,
    // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
    // the end of an archive for linkers to not get confused.
    entries: Vec<(String, ArchiveEntry)>,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        use rustc_codegen_ssa::back::link::archive_search_paths;

        let (src_archives, entries) = if let Some(input) = input {
            let mut archive = ar::Archive::new(File::open(input).unwrap());
            let mut entries = Vec::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.push((
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive { archive_index: 0, entry_index: i },
                ));
                i += 1;
            }

            (vec![(input.to_owned(), archive)], entries)
        } else {
            (vec![], Vec::new())
        };

        ArArchiveBuilder {
            sess,
            dst: output.to_path_buf(),
            lib_search_paths: archive_search_paths(sess),
            use_gnu_style_archive: sess.target.archive_format == "gnu",
            // FIXME fix builtin ranlib on macOS
            no_builtin_ranlib: sess.target.is_like_osx,

            src_archives,
            entries,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.iter().map(|(name, _)| name.clone()).collect()
    }

    fn remove_file(&mut self, name: &str) {
        let index = self
            .entries
            .iter()
            .position(|(entry_name, _)| entry_name == name)
            .expect("Tried to remove file not existing in src archive");
        self.entries.remove(index);
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.push((
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(file.to_owned()),
        ));
    }

    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol, verbatim: bool) {
        let location = find_library(name, verbatim, &self.lib_search_paths, self.sess);
        self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| {
            panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
        });
    }

    fn add_rlib(
        &mut self,
        rlib: &Path,
        name: &str,
        lto: bool,
        skip_objects: bool,
    ) -> std::io::Result<()> {
        let obj_start = name.to_owned();

        self.add_archive(rlib.to_owned(), move |fname: &str| {
            // Ignore metadata files, no matter the name.
            if fname == METADATA_FILENAME {
                return true;
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true;
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true;
            }

            // ok, don't skip this
            false
        })
    }

    fn update_symbols(&mut self) {}

    fn build(mut self) {
        enum BuilderKind {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
        }

        let sess = self.sess;

        let mut symbol_table = BTreeMap::new();

        let mut entries = Vec::new();

        for (entry_name, entry) in self.entries {
            // FIXME only read the symbol table of the object files to avoid having to keep all
            // object files in memory at once, or read them twice.
            let data = match entry {
                ArchiveEntry::FromArchive { archive_index, entry_index } => {
                    // FIXME read symbols from symtab
                    use std::io::Read;
                    let (ref _src_archive_path, ref mut src_archive) =
                        self.src_archives[archive_index];
                    let mut entry = src_archive.jump_to_entry(entry_index).unwrap();
                    let mut data = Vec::new();
                    entry.read_to_end(&mut data).unwrap();
                    data
                }
                ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| {
                    sess.fatal(&format!(
                        "error while reading object file during archive building: {}",
                        err
                    ));
                }),
            };

            if !self.no_builtin_ranlib {
                match object::File::parse(&*data) {
                    Ok(object) => {
                        symbol_table.insert(
                            entry_name.as_bytes().to_vec(),
                            object
                                .symbols()
                                .filter_map(|symbol| {
                                    if symbol.is_undefined()
                                        || symbol.is_local()
                                        || symbol.kind() != SymbolKind::Data
                                            && symbol.kind() != SymbolKind::Text
                                            && symbol.kind() != SymbolKind::Tls
                                    {
                                        None
                                    } else {
                                        symbol.name().map(|name| name.as_bytes().to_vec()).ok()
                                    }
                                })
                                .collect::<Vec<_>>(),
                        );
                    }
                    Err(err) => {
                        let err = err.to_string();
                        if err == "Unknown file magic" {
                            // Not an object file; skip it.
                        } else {
                            sess.fatal(&format!(
                                "error parsing `{}` during archive creation: {}",
                                entry_name, err
                            ));
                        }
                    }
                }
            }

            entries.push((entry_name, data));
        }

        let mut builder = if self.use_gnu_style_archive {
            BuilderKind::Gnu(
                ar::GnuBuilder::new(
                    File::create(&self.dst).unwrap_or_else(|err| {
                        sess.fatal(&format!(
                            "error opening destination during archive building: {}",
                            err
                        ));
                    }),
                    entries.iter().map(|(name, _)| name.as_bytes().to_vec()).collect(),
                    ar::GnuSymbolTableFormat::Size32,
                    symbol_table,
                )
                .unwrap(),
            )
        } else {
            BuilderKind::Bsd(
                ar::Builder::new(
                    File::create(&self.dst).unwrap_or_else(|err| {
                        sess.fatal(&format!(
                            "error opening destination during archive building: {}",
                            err
                        ));
                    }),
                    symbol_table,
                )
                .unwrap(),
            )
        };

        // Add all files
        for (entry_name, data) in entries.into_iter() {
            let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64);
            match builder {
                BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
                BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        if self.no_builtin_ranlib {
            let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib");

            // Run ranlib to be able to link the archive
            let status = std::process::Command::new(ranlib)
                .arg(self.dst)
                .status()
                .expect("Couldn't run ranlib");

            if !status.success() {
                self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
            }
        }
    }

    fn inject_dll_import_lib(
        &mut self,
        _lib_name: &str,
        _dll_imports: &[rustc_middle::middle::cstore::DllImport],
        _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir,
    ) {
        bug!("injecting dll imports is not supported");
    }
}

impl<'a> ArArchiveBuilder<'a> {
    fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()>
    where
        F: FnMut(&str) -> bool + 'static,
    {
        let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry?;
            let file_name = String::from_utf8(entry.header().identifier().to_vec())
                .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
            if !skip(&file_name) {
                self.entries
                    .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
            }
            i += 1;
        }

        self.src_archives.push((archive_path, archive));
        Ok(())
    }
}
<commit_msg>Move add_rlib and add_native_library to cg_ssa<commit_after>//! Creation of ar archives like for the lib and staticlib crate type

use std::collections::BTreeMap;
use std::fs::File;
use std::path::{Path, PathBuf};

use rustc_codegen_ssa::back::archive::ArchiveBuilder;
use rustc_session::Session;

use object::{Object, ObjectSymbol, SymbolKind};

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive { archive_index: usize, entry_index: usize },
    File(PathBuf),
}

pub(crate) struct ArArchiveBuilder<'a> {
    sess: &'a Session,
    dst: PathBuf,
    use_gnu_style_archive: bool,
    no_builtin_ranlib: bool,

    src_archives: Vec<(PathBuf, ar::Archive<File>)>,
    // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
    // the end of an archive for linkers to not get confused.
    entries: Vec<(String, ArchiveEntry)>,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        let (src_archives, entries) = if let Some(input) = input {
            let mut archive = ar::Archive::new(File::open(input).unwrap());
            let mut entries = Vec::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.push((
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive { archive_index: 0, entry_index: i },
                ));
                i += 1;
            }

            (vec![(input.to_owned(), archive)], entries)
        } else {
            (vec![], Vec::new())
        };

        ArArchiveBuilder {
            sess,
            dst: output.to_path_buf(),
            use_gnu_style_archive: sess.target.archive_format == "gnu",
            // FIXME fix builtin ranlib on macOS
            no_builtin_ranlib: sess.target.is_like_osx,

            src_archives,
            entries,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.iter().map(|(name, _)| name.clone()).collect()
    }

    fn remove_file(&mut self, name: &str) {
        let index = self
            .entries
            .iter()
            .position(|(entry_name, _)| entry_name == name)
            .expect("Tried to remove file not existing in src archive");
        self.entries.remove(index);
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.push((
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(file.to_owned()),
        ));
    }

    fn add_archive<F>(&mut self, archive_path: &Path, mut skip: F) -> std::io::Result<()>
    where
        F: FnMut(&str) -> bool + 'static,
    {
        let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry?;
            let file_name = String::from_utf8(entry.header().identifier().to_vec())
                .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
            if !skip(&file_name) {
                self.entries
                    .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
            }
            i += 1;
        }

        self.src_archives.push((archive_path.to_owned(), archive));
        Ok(())
    }

    fn update_symbols(&mut self) {}

    fn build(mut self) {
        enum BuilderKind {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
        }

        let sess = self.sess;

        let mut symbol_table = BTreeMap::new();

        let mut entries = Vec::new();

        for (entry_name, entry) in self.entries {
            // FIXME only read the symbol table of the object files to avoid having to keep all
            // object files in memory at once, or read them twice.
            let data = match entry {
                ArchiveEntry::FromArchive { archive_index, entry_index } => {
                    // FIXME read symbols from symtab
                    use std::io::Read;
                    let (ref _src_archive_path, ref mut src_archive) =
                        self.src_archives[archive_index];
                    let mut entry = src_archive.jump_to_entry(entry_index).unwrap();
                    let mut data = Vec::new();
                    entry.read_to_end(&mut data).unwrap();
                    data
                }
                ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| {
                    sess.fatal(&format!(
                        "error while reading object file during archive building: {}",
                        err
                    ));
                }),
            };

            if !self.no_builtin_ranlib {
                match object::File::parse(&*data) {
                    Ok(object) => {
                        symbol_table.insert(
                            entry_name.as_bytes().to_vec(),
                            object
                                .symbols()
                                .filter_map(|symbol| {
                                    if symbol.is_undefined()
                                        || symbol.is_local()
                                        || symbol.kind() != SymbolKind::Data
                                            && symbol.kind() != SymbolKind::Text
                                            && symbol.kind() != SymbolKind::Tls
                                    {
                                        None
                                    } else {
                                        symbol.name().map(|name| name.as_bytes().to_vec()).ok()
                                    }
                                })
                                .collect::<Vec<_>>(),
                        );
                    }
                    Err(err) => {
                        let err = err.to_string();
                        if err == "Unknown file magic" {
                            // Not an object file; skip it.
                        } else {
                            sess.fatal(&format!(
                                "error parsing `{}` during archive creation: {}",
                                entry_name, err
                            ));
                        }
                    }
                }
            }

            entries.push((entry_name, data));
        }

        let mut builder = if self.use_gnu_style_archive {
            BuilderKind::Gnu(
                ar::GnuBuilder::new(
                    File::create(&self.dst).unwrap_or_else(|err| {
                        sess.fatal(&format!(
                            "error opening destination during archive building: {}",
                            err
                        ));
                    }),
                    entries.iter().map(|(name, _)| name.as_bytes().to_vec()).collect(),
                    ar::GnuSymbolTableFormat::Size32,
                    symbol_table,
                )
                .unwrap(),
            )
        } else {
            BuilderKind::Bsd(
                ar::Builder::new(
                    File::create(&self.dst).unwrap_or_else(|err| {
                        sess.fatal(&format!(
                            "error opening destination during archive building: {}",
                            err
                        ));
                    }),
                    symbol_table,
                )
                .unwrap(),
            )
        };

        // Add all files
        for (entry_name, data) in entries.into_iter() {
            let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64);
            match builder {
                BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
                BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        if self.no_builtin_ranlib {
            let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib");

            // Run ranlib to be able to link the archive
            let status = std::process::Command::new(ranlib)
                .arg(self.dst)
                .status()
                .expect("Couldn't run ranlib");

            if !status.success() {
                self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
            }
        }
    }

    fn inject_dll_import_lib(
        &mut self,
        _lib_name: &str,
        _dll_imports: &[rustc_middle::middle::cstore::DllImport],
        _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir,
    ) {
        bug!("injecting dll imports is not supported");
    }
}
<|endoftext|>