<commit_before>use {SendDgram, RecvDgram};

use tokio_reactor::{Handle, PollEvented};

use futures::{Async, Poll};
use mio::Ready;
use mio_uds;

use std::fmt;
use std::io;
use std::net::Shutdown;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;

/// An I/O object representing a Unix datagram socket.
pub struct UnixDatagram {
    io: PollEvented<mio_uds::UnixDatagram>,
}

impl UnixDatagram {
    /// Creates a new `UnixDatagram` bound to the specified path.
    pub fn bind<P>(path: P) -> io::Result<UnixDatagram>
    where
        P: AsRef<Path>,
    {
        let socket = mio_uds::UnixDatagram::bind(path)?;
        Ok(UnixDatagram::new(socket))
    }

    /// Creates an unnamed pair of connected sockets.
    ///
    /// This function will create a pair of interconnected unix sockets for
    /// communicating back and forth between one another. Each socket will be
    /// associated with the event loop whose handle is also provided.
    pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
        let (a, b) = mio_uds::UnixDatagram::pair()?;
        let a = UnixDatagram::new(a);
        let b = UnixDatagram::new(b);

        Ok((a, b))
    }

    /// Consumes a `UnixDatagram` in the standard library and returns a
    /// nonblocking `UnixDatagram` from this crate.
    ///
    /// The returned datagram will be associated with the given event loop
    /// specified by `handle` and is ready to perform I/O.
    pub fn from_std(datagram: net::UnixDatagram, handle: &Handle) -> io::Result<UnixDatagram> {
        let socket = mio_uds::UnixDatagram::from_datagram(datagram)?;
        let io = PollEvented::new_with_handle(socket, handle)?;
        Ok(UnixDatagram { io })
    }

    fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram {
        let io = PollEvented::new(socket);
        UnixDatagram { io }
    }

    /// Creates a new `UnixDatagram` which is not bound to any address.
    pub fn unbound() -> io::Result<UnixDatagram> {
        let socket = mio_uds::UnixDatagram::unbound()?;
        Ok(UnixDatagram::new(socket))
    }

    /// Connects the socket to the specified address.
    ///
    /// The `send` method may be used to send data to the specified address.
    /// `recv` and `recv_from` will only receive data from that address.
    pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        self.io.get_ref().connect(path)
    }

    /// Test whether this socket is ready to be read or not.
    pub fn poll_read_ready(&self, ready: Ready) -> Poll<Ready, io::Error> {
        self.io.poll_read_ready(ready)
    }

    /// Test whether this socket is ready to be written to or not.
    pub fn poll_write_ready(&self) -> Poll<Ready, io::Error> {
        self.io.poll_write_ready()
    }

    /// Returns the local address that this socket is bound to.
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.io.get_ref().local_addr()
    }

    /// Returns the address of this socket's peer.
    ///
    /// The `connect` method will connect the socket to a peer.
    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        self.io.get_ref().peer_addr()
    }

    /// Receives data from the socket.
    ///
    /// On success, returns the number of bytes read and the address from
    /// whence the data came.
    pub fn poll_recv_from(&self, buf: &mut [u8]) -> Poll<(usize, SocketAddr), io::Error> {
        if self.io.poll_read_ready(Ready::readable())?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().recv_from(buf);
        if is_wouldblock(&r) {
            self.io.clear_read_ready(Ready::readable())?;
        }
        r.map(Async::Ready)
    }

    /// Receives data from the socket.
    ///
    /// On success, returns the number of bytes read.
    pub fn poll_recv(&self, buf: &mut [u8]) -> Poll<usize, io::Error> {
        if self.io.poll_read_ready(Ready::readable())?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().recv(buf);
        if is_wouldblock(&r) {
            self.io.clear_read_ready(Ready::readable())?;
        }
        r.map(Async::Ready)
    }

    /// Returns a future for receiving a datagram. See the documentation on RecvDgram for details.
    pub fn recv_dgram<T>(self, buf: T) -> RecvDgram<T>
    where
        T: AsMut<[u8]>,
    {
        RecvDgram::new(self, buf)
    }

    /// Sends data on the socket to the specified address.
    ///
    /// On success, returns the number of bytes written.
    pub fn poll_send_to<P>(&self, buf: &[u8], path: P) -> Poll<usize, io::Error>
    where
        P: AsRef<Path>,
    {
        if self.io.poll_write_ready()?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().send_to(buf, path);
        if is_wouldblock(&r) {
            self.io.clear_write_ready()?;
        }
        r.map(Async::Ready)
    }

    /// Sends data on the socket to the socket's peer.
    ///
    /// The peer address may be set by the `connect` method, and this method
    /// will return an error if the socket has not already been connected.
    ///
    /// On success, returns the number of bytes written.
    pub fn poll_send(&self, buf: &[u8]) -> Poll<usize, io::Error> {
        if self.io.poll_write_ready()?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().send(buf);
        if is_wouldblock(&r) {
            self.io.clear_write_ready()?;
        }
        r.map(Async::Ready)
    }

    /// Returns a future sending the data in buf to the socket at path.
    pub fn send_dgram<T, P>(self, buf: T, path: P) -> SendDgram<T, P>
    where
        T: AsRef<[u8]>,
        P: AsRef<Path>,
    {
        SendDgram::new(self, buf, path)
    }

    /// Returns the value of the `SO_ERROR` option.
    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        self.io.get_ref().take_error()
    }

    /// Shut down the read, write, or both halves of this connection.
    ///
    /// This function will cause all pending and future I/O calls on the
    /// specified portions to immediately return with an appropriate value
    /// (see the documentation of `Shutdown`).
    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        self.io.get_ref().shutdown(how)
    }
}

impl fmt::Debug for UnixDatagram {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.io.get_ref().fmt(f)
    }
}

impl AsRawFd for UnixDatagram {
    fn as_raw_fd(&self) -> RawFd {
        self.io.get_ref().as_raw_fd()
    }
}

fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
    match *r {
        Ok(_) => false,
        Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
    }
}
<commit_msg>return NotReady when recv_from wouldblock in uds (#452)<commit_after>use {SendDgram, RecvDgram};

use tokio_reactor::{Handle, PollEvented};

use futures::{Async, Poll};
use mio::Ready;
use mio_uds;

use std::fmt;
use std::io;
use std::net::Shutdown;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;

/// An I/O object representing a Unix datagram socket.
pub struct UnixDatagram {
    io: PollEvented<mio_uds::UnixDatagram>,
}

impl UnixDatagram {
    /// Creates a new `UnixDatagram` bound to the specified path.
    pub fn bind<P>(path: P) -> io::Result<UnixDatagram>
    where
        P: AsRef<Path>,
    {
        let socket = mio_uds::UnixDatagram::bind(path)?;
        Ok(UnixDatagram::new(socket))
    }

    /// Creates an unnamed pair of connected sockets.
    ///
    /// This function will create a pair of interconnected unix sockets for
    /// communicating back and forth between one another. Each socket will be
    /// associated with the event loop whose handle is also provided.
    pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
        let (a, b) = mio_uds::UnixDatagram::pair()?;
        let a = UnixDatagram::new(a);
        let b = UnixDatagram::new(b);

        Ok((a, b))
    }

    /// Consumes a `UnixDatagram` in the standard library and returns a
    /// nonblocking `UnixDatagram` from this crate.
    ///
    /// The returned datagram will be associated with the given event loop
    /// specified by `handle` and is ready to perform I/O.
    pub fn from_std(datagram: net::UnixDatagram, handle: &Handle) -> io::Result<UnixDatagram> {
        let socket = mio_uds::UnixDatagram::from_datagram(datagram)?;
        let io = PollEvented::new_with_handle(socket, handle)?;
        Ok(UnixDatagram { io })
    }

    fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram {
        let io = PollEvented::new(socket);
        UnixDatagram { io }
    }

    /// Creates a new `UnixDatagram` which is not bound to any address.
    pub fn unbound() -> io::Result<UnixDatagram> {
        let socket = mio_uds::UnixDatagram::unbound()?;
        Ok(UnixDatagram::new(socket))
    }

    /// Connects the socket to the specified address.
    ///
    /// The `send` method may be used to send data to the specified address.
    /// `recv` and `recv_from` will only receive data from that address.
    pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        self.io.get_ref().connect(path)
    }

    /// Test whether this socket is ready to be read or not.
    pub fn poll_read_ready(&self, ready: Ready) -> Poll<Ready, io::Error> {
        self.io.poll_read_ready(ready)
    }

    /// Test whether this socket is ready to be written to or not.
    pub fn poll_write_ready(&self) -> Poll<Ready, io::Error> {
        self.io.poll_write_ready()
    }

    /// Returns the local address that this socket is bound to.
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.io.get_ref().local_addr()
    }

    /// Returns the address of this socket's peer.
    ///
    /// The `connect` method will connect the socket to a peer.
    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        self.io.get_ref().peer_addr()
    }

    /// Receives data from the socket.
    ///
    /// On success, returns the number of bytes read and the address from
    /// whence the data came.
    pub fn poll_recv_from(&self, buf: &mut [u8]) -> Poll<(usize, SocketAddr), io::Error> {
        if self.io.poll_read_ready(Ready::readable())?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().recv_from(buf);
        if is_wouldblock(&r) {
            self.io.clear_read_ready(Ready::readable())?;
            return Ok(Async::NotReady);
        }
        r.map(Async::Ready)
    }

    /// Receives data from the socket.
    ///
    /// On success, returns the number of bytes read.
    pub fn poll_recv(&self, buf: &mut [u8]) -> Poll<usize, io::Error> {
        if self.io.poll_read_ready(Ready::readable())?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().recv(buf);
        if is_wouldblock(&r) {
            self.io.clear_read_ready(Ready::readable())?;
            return Ok(Async::NotReady);
        }
        r.map(Async::Ready)
    }

    /// Returns a future for receiving a datagram. See the documentation on RecvDgram for details.
    pub fn recv_dgram<T>(self, buf: T) -> RecvDgram<T>
    where
        T: AsMut<[u8]>,
    {
        RecvDgram::new(self, buf)
    }

    /// Sends data on the socket to the specified address.
    ///
    /// On success, returns the number of bytes written.
    pub fn poll_send_to<P>(&self, buf: &[u8], path: P) -> Poll<usize, io::Error>
    where
        P: AsRef<Path>,
    {
        if self.io.poll_write_ready()?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().send_to(buf, path);
        if is_wouldblock(&r) {
            self.io.clear_write_ready()?;
        }
        r.map(Async::Ready)
    }

    /// Sends data on the socket to the socket's peer.
    ///
    /// The peer address may be set by the `connect` method, and this method
    /// will return an error if the socket has not already been connected.
    ///
    /// On success, returns the number of bytes written.
    pub fn poll_send(&self, buf: &[u8]) -> Poll<usize, io::Error> {
        if self.io.poll_write_ready()?.is_not_ready() {
            return Ok(Async::NotReady);
        }
        let r = self.io.get_ref().send(buf);
        if is_wouldblock(&r) {
            self.io.clear_write_ready()?;
        }
        r.map(Async::Ready)
    }

    /// Returns a future sending the data in buf to the socket at path.
    pub fn send_dgram<T, P>(self, buf: T, path: P) -> SendDgram<T, P>
    where
        T: AsRef<[u8]>,
        P: AsRef<Path>,
    {
        SendDgram::new(self, buf, path)
    }

    /// Returns the value of the `SO_ERROR` option.
    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        self.io.get_ref().take_error()
    }

    /// Shut down the read, write, or both halves of this connection.
    ///
    /// This function will cause all pending and future I/O calls on the
    /// specified portions to immediately return with an appropriate value
    /// (see the documentation of `Shutdown`).
    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        self.io.get_ref().shutdown(how)
    }
}

impl fmt::Debug for UnixDatagram {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.io.get_ref().fmt(f)
    }
}

impl AsRawFd for UnixDatagram {
    fn as_raw_fd(&self) -> RawFd {
        self.io.get_ref().as_raw_fd()
    }
}

fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
    match *r {
        Ok(_) => false,
        Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
    }
}
<|endoftext|>


<commit_before>use self::super::util::{human_readable_size, parent_url, GETCH_ARROW_PREFIX, GETCH_ARROW_RIGHT, GETCH_ARROW_LEFT, GETCH_ARROW_DOWN, GETCH_ARROW_UP, TAB_SPACING,
                        GETCH_ENTER, USER_AGENT, GETCH_ESC};
use rfsapi::{RawFsApiHeader, FilesetData, RawFileData};
use std::io::{self, BufReader, BufRead, Write, Read};
use reqwest::{Response, IntoUrl, Client, Url};
use reqwest::header::UserAgent;
use itertools::Itertools;
use tabwriter::TabWriter;
use std::path::Path;
use std::{cmp, fmt};
use std::fs::File;
use getch::Getch;
use time::Tm;

pub mod term;

/// GET a resource with the RFSAPI header, auto-unpacking GZip.
pub fn download<U: IntoUrl>(u: U) -> Response {
    really_download(u, true)
}

/// GET a resource normally, auto-unpacking GZip.
pub fn download_raw<U: IntoUrl>(u: U) -> Response {
    really_download(u, false)
}

fn really_download<U: IntoUrl>(u: U, raw: bool) -> Response {
    let mut client = Client::new().unwrap();
    client.gzip(true);
    client.get(u)
        .header(RawFsApiHeader(raw))
        .header(UserAgent(USER_AGENT.to_string()))
        .send()
        .unwrap()
}

/// Copy `label`led data from `reader` to `writer`, stopping each screen, wrapping at `term_size`,
/// polling `input` for instructions.
///
/// Never panics, returns `Ok(true)` if paging succeeded or `Ok(false)` if paged data isn't UTF-8.
///
/// ### Controls on nonlast pages
///
/// Key|Result
/// ---|------
/// Escape | stop paging
/// Any key | go to next page
///
/// Controls on last page:
///
/// Key|Result
/// ---|------
/// Any key | stop paging
pub fn paging_copy<R: Read, W: Write>(reader: &mut R, writer: &mut W, label: &str, input: &Getch, term_size: (usize, usize)) -> io::Result<bool> {
    const END_MESSAGE_RAW_LEN: usize = 31 + 4 + 22 + 2 + 1 + 2 + 1;

    let (tx, ty) = term_size;
    let mut reader = BufReader::new(reader);
    let mut outlines: Vec<String> = vec![];

    let mut line = String::new();
    while match reader.read_line(&mut line) {
        Ok(read) => read != 0,
        Err(e) => {
            return if e.kind() == io::ErrorKind::InvalidData {
                Ok(false)
            } else {
                Err(e)
            };
        }
    } {
        line = line.replace(&['\r', '\n'][..], "").replace('\t', &TAB_SPACING);
        if line.is_empty() {
            outlines.push(String::new());
        } else {
            outlines.extend(line.drain(..).chunks(tx).into_iter().map(|l| l.collect()));
        }
    }
    let _ = line;

    let end_message_len = END_MESSAGE_RAW_LEN + label.chars().count();
    let end_message_lines = (end_message_len as f64 / tx as f64).floor() as usize;
    let screen_count = (outlines.len() as f64 / (ty - end_message_lines) as f64).ceil() as usize;
    for (si, screen) in outlines.chunks(ty - end_message_lines).enumerate() {
        if si != 0 {
            try!(writeln!(writer));
        }
        for line in screen {
            try!(writeln!(writer, "{}", line));
        }

        try!(write!(writer, "<{}> ", label));
        if si == screen_count - 1 {
            try!(write!(writer, "<End of file> <Press any key to stop>"));
        } else {
            try!(write!(writer,
                        "<{}%> <Press any key for {} screen> <Press Escape to stop>",
                        ((si + 1) as f64 / screen_count as f64 * 100f64).round(),
                        if si + 1 == screen_count - 1 {
                            "last"
                        } else {
                            "next"
                        }));
        }
        try!(writer.flush());

        if input.getch().unwrap() == GETCH_ESC {
            break;
        }
    }

    try!(writeln!(writer));
    Ok(true)
}

/// Main context used to list a server.
pub struct ListContext {
    cururl: Url,
    files: Vec<RemoteFile>,
    selected: usize,
}

impl ListContext {
    /// Create a context, starting off at the provided URL.
    pub fn new(starting_url: Url) -> ListContext {
        ListContext {
            cururl: starting_url,
            files: vec![],
            selected: 0,
        }
    }

    /// Do one run of the input loop, effectively resulting in a single action.
    ///
    /// Returns `Ok(true)` to allow to continue next loop or `Ok(false)` to end listing.
    ///
    /// ### Commands
    ///
    /// Key|Result
    /// -------|-------
    /// Enter/Right Arrow | enter highlighted entry
    /// Escape/`'Q'`/`'q'` | end
    /// `'D'`/`'d'` | download file
    /// Up Arrow | move selection 1 entry up
    /// Down Arrow | move selection 1 entry down
    /// Left Arrow | go up one level, if not at root
    ///
    /// ### Entering entries
    ///
    /// If the entry is a directory, on the next loop the selected subdirectory wll be listed.
    ///
    /// If the entry is a UTF-8 file, its contents are paged, see [`paging_copy()`](fn.paging_copy.html).
    ///
    /// If the entry is a non-UTF-8 file, it is [downloaded](#downloading-files).
    ///
    /// ### Downloading files
    ///
    /// The user is shown a file picker and let choose where to download the file to,
    /// then the file is downloaded and saved to the specified location.
    ///
    /// The file isn't saved if the user cancels the picker.
    pub fn one_loop<W: Write>(&mut self, mut out: &mut W, input: &Getch, term_size: (usize, usize)) -> io::Result<bool> {
        try!(writeln!(out, "Contents of {}:", self.cururl));
        let mut resp = download(self.cururl.clone());
        if !resp.status().is_success() {
            try!(writeln!(out, "<Got {}...>", resp.status()));
            self.back();
            return Ok(true);
        }

        let data = match resp.json::<FilesetData>() {
            Ok(d) => d,
            Err(e) => {
                try!(writeln!(out, "<Couldn't parse server response: {}...>", e));
                self.back();
                return Ok(true);
            }
        };

        if data.is_file {
            if !try!(paging_copy(&mut download_raw(self.cururl.clone()), out, &self.cururl.path()[1..], &input, term_size)) {
                try!(writeln!(out, "<Not UTF-8, select download destination>"));
                try!(self.download_file(out));
            }
            self.cururl = parent_url(&self.cururl);
        } else {
            self.files = RemoteFile::from_response(data);
            {
                let mut tout = TabWriter::new(&mut out);
                for (i, f) in self.files.iter().enumerate() {
                    try!(writeln!(tout, "{}{}", if i == self.selected { ">" } else { " " }, f));
                }
                try!(tout.flush());
            }

            while let (true, exit) = try!(self.process_input(out, input)) {
                if exit {
                    return Ok(false);
                }
            }
        }

        Ok(true)
    }

    fn process_input<W: Write>(&mut self, out: &mut W, input: &Getch) -> io::Result<(bool, bool)> {
        match try!(input.getch()) {
            GETCH_ENTER => Ok((self.select(), false)),
            GETCH_ESC | b'q' | b'Q' => Ok((true, true)),
            b'd' | b'D' => {
                try!(self.download_file(out));
                Ok((false, false))
            }
            GETCH_ARROW_PREFIX => {
                match try!(input.getch()) {
                    GETCH_ARROW_UP => {
                        if self.selected != 0 {
                            try!(self.update_selected(out, ' '));
                            self.selected -= 1;
                            try!(self.update_selected(out, '>'));
                        }
                        Ok((true, false))
                    }
                    GETCH_ARROW_DOWN => {
                        try!(self.update_selected(out, ' '));
                        self.selected = cmp::min(self.selected + 1, self.files.len() - 1);
                        try!(self.update_selected(out, '>'));
                        Ok((true, false))
                    }
                    GETCH_ARROW_LEFT => Ok((self.back(), false)),
                    GETCH_ARROW_RIGHT => Ok((self.select(), false)),
                    _ => Ok((true, false)),
                }
            }
            _ => Ok((true, false)),
        }
    }

    fn update_selected<W: Write>(&self, out: &mut W, c: char) -> io::Result<()> {
        try!(write!(out, "{}", term::move_cursor_up(self.files.len() - self.selected)));
        try!(write!(out, "{}", c));
        try!(out.flush());
        try!(write!(out, "{}", term::move_cursor_back(1)));
        try!(write!(out, "{}", term::move_cursor_down(self.files.len() - self.selected)));
        Ok(())
    }

    fn select(&mut self) -> bool {
        if !self.files.is_empty() {
            self.cururl = self.cururl.join(&self.files[self.selected].full_name).unwrap();
            self.selected = 0;
        }
        self.files.is_empty()
    }

    fn back(&mut self) -> bool {
        let parent = parent_url(&self.cururl);
        if parent != self.cururl {
            self.cururl = parent;
            self.selected = 0;
            true
        } else {
            false
        }
    }

    fn download_file<W: Write>(&self, out: &mut W) -> io::Result<()> {
        let f = Path::new(&self.cururl.path()[1..]);
        if let Some(outp) = term::save_file_picker(f.file_name().unwrap(), f.extension()) {
            try!(writeln!(out, "<Downloading to {}...>", outp.display()));
            try!(io::copy(&mut download_raw(self.cururl.clone()), &mut try!(File::create(&outp))));
            try!(writeln!(out, "<Done!>"));
        }
        Ok(())
    }
}

/// Parsed `RawFileData` entry, prepared for end-user usage.
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct RemoteFile {
    /// Resource's name, ends with `'/'` if it's a directory.
    pub full_name: String,
    /// File size in bytes, `None` for directories.
    pub size: Option<u64>,
    /// Human-readable file size, `None` for directories.
    ///
    /// See [`util::human_readable_size()`](../util/fn.human_readable_size.html).
    pub human_size: Option<String>,
    /// Last modification time in local TZ, `None` for special `"../"` entry.
    pub last_modified: Option<Tm>,
}

impl RemoteFile {
    /// Parse a server-returned fileset into a set of user-viewable files.
    ///
    /// The returned vector is empty if the specified fileset describes a file.
    ///
    /// The returned vector contains an additional entry at the front with name `"../"` if the fileset isn't a root directory.
    ///
    /// The returned vector is partitioned according depending on directoriness status of the entry, directories first.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate rfsapi;
    /// # extern crate doh;
    /// # use doh::ops::RemoteFile;
    /// # use doh::util::parse_rfc3339;
    /// # use rfsapi::{FilesetData, RawFileData};
    /// # fn main() {
    /// assert_eq!(RemoteFile::from_response(FilesetData {
    ///     writes_supported: false,
    ///     is_root: false,
    ///     is_file: false,
    ///     files: vec![
    ///         RawFileData {
    ///             mime_type: "text/directory".parse().unwrap(),
    ///             name: "data".to_string(),
    ///             last_modified: parse_rfc3339("2012-02-22T14:53:18.42Z").unwrap(),
    ///             size: 0,
    ///             is_file: false,
    ///         },
    ///         RawFileData {
    ///             mime_type: "text/html".parse().unwrap(),
    ///             name: "index.html".to_string(),
    ///             last_modified: parse_rfc3339("2012-02-22T15:23:18Z").unwrap(),
    ///             size: 2297,
    ///             is_file: true,
    ///         },
    ///     ],
    /// }), vec![
    ///     RemoteFile {
    ///         full_name: "../".to_string(),
    ///         size: None,
    ///         human_size: None,
    ///         last_modified: None,
    ///     },
    ///     RemoteFile {
    ///         full_name: "data/".to_string(),
    ///         size: None,
    ///         human_size: None,
    ///         last_modified: Some(parse_rfc3339("2012-02-22T14:53:18.42Z").unwrap().to_local()),
    ///     },
    ///     RemoteFile {
    ///         full_name: "index.html".to_string(),
    ///         size: Some(2297),
    ///         human_size: Some("2.2KiB".to_string()),
    ///         last_modified: Some(parse_rfc3339("2012-02-22T15:23:18Z").unwrap().to_local()),
    ///     },
    /// ]);
    /// # }
    /// ```
    pub fn from_response(mut resp: FilesetData) -> Vec<RemoteFile> {
        if resp.is_file {
            vec![]
        } else {
            resp.files.sort_by_key(|f| f.is_file);
            let mut fs: Vec<_> = resp.files
                .into_iter()
                .map(From::from)
                .collect();
            if !resp.is_root {
                fs.insert(0,
                          RemoteFile {
                              full_name: "../".to_string(),
                              size: None,
                              human_size: None,
                              last_modified: None,
                          });
            }
            fs
        }
    }
}

impl From<RawFileData> for RemoteFile {
    fn from(rfd: RawFileData) -> RemoteFile {
        RemoteFile {
            full_name: rfd.name + if rfd.is_file { "" } else { "/" },
            size: if rfd.is_file { Some(rfd.size) } else { None },
            human_size: if rfd.is_file {
                Some(human_readable_size(rfd.size))
            } else {
                None
            },
            last_modified: Some(rfd.last_modified.to_local()),
        }
    }
}

/// Designed for use with `TabWriter`, separates entries with `\t`s.
impl fmt::Display for RemoteFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}\t", self.full_name));
        if let Some(ref hs) = self.human_size.as_ref() {
            try!(write!(f, "{}", hs));
        }
        try!(write!(f, "\t"));
        if let Some(ref lm) = self.last_modified.as_ref() {
            try!(write!(f, "{}", lm.rfc3339()));
        }
        Ok(())
    }
}
<commit_msg>Fix explicit downloads<commit_after>use self::super::util::{human_readable_size, parent_url, GETCH_ARROW_PREFIX, GETCH_ARROW_RIGHT, GETCH_ARROW_LEFT, GETCH_ARROW_DOWN, GETCH_ARROW_UP, TAB_SPACING,
                        GETCH_ENTER, USER_AGENT, GETCH_ESC};
use rfsapi::{RawFsApiHeader, FilesetData, RawFileData};
use std::io::{self, BufReader, BufRead, Write, Read};
use reqwest::{Response, IntoUrl, Client, Url};
use reqwest::header::UserAgent;
use itertools::Itertools;
use tabwriter::TabWriter;
use std::path::PathBuf;
use std::{cmp, fmt};
use std::fs::File;
use getch::Getch;
use time::Tm;

pub mod term;

/// GET a resource with the RFSAPI header, auto-unpacking GZip.
pub fn download<U: IntoUrl>(u: U) -> Response {
    really_download(u, true)
}

/// GET a resource normally, auto-unpacking GZip.
pub fn download_raw<U: IntoUrl>(u: U) -> Response {
    really_download(u, false)
}

fn really_download<U: IntoUrl>(u: U, raw: bool) -> Response {
    let mut client = Client::new().unwrap();
    client.gzip(true);
    client.get(u)
        .header(RawFsApiHeader(raw))
        .header(UserAgent(USER_AGENT.to_string()))
        .send()
        .unwrap()
}

/// Copy `label`led data from `reader` to `writer`, stopping each screen, wrapping at `term_size`,
/// polling `input` for instructions.
///
/// Never panics, returns `Ok(true)` if paging succeeded or `Ok(false)` if paged data isn't UTF-8.
///
/// ### Controls on nonlast pages
///
/// Key|Result
/// ---|------
/// Escape | stop paging
/// Any key | go to next page
///
/// Controls on last page:
///
/// Key|Result
/// ---|------
/// Any key | stop paging
pub fn paging_copy<R: Read, W: Write>(reader: &mut R, writer: &mut W, label: &str, input: &Getch, term_size: (usize, usize)) -> io::Result<bool> {
    const END_MESSAGE_RAW_LEN: usize = 31 + 4 + 22 + 2 + 1 + 2 + 1;

    let (tx, ty) = term_size;
    let mut reader = BufReader::new(reader);
    let mut outlines: Vec<String> = vec![];

    let mut line = String::new();
    while match reader.read_line(&mut line) {
        Ok(read) => read != 0,
        Err(e) => {
            return if e.kind() == io::ErrorKind::InvalidData {
                Ok(false)
            } else {
                Err(e)
            };
        }
    } {
        line = line.replace(&['\r', '\n'][..], "").replace('\t', &TAB_SPACING);
        if line.is_empty() {
            outlines.push(String::new());
        } else {
            outlines.extend(line.drain(..).chunks(tx).into_iter().map(|l| l.collect()));
        }
    }
    let _ = line;

    let end_message_len = END_MESSAGE_RAW_LEN + label.chars().count();
    let end_message_lines = (end_message_len as f64 / tx as f64).floor() as usize;
    let screen_count = (outlines.len() as f64 / (ty - end_message_lines) as f64).ceil() as usize;
    for (si, screen) in outlines.chunks(ty - end_message_lines).enumerate() {
        if si != 0 {
            try!(writeln!(writer));
        }
        for line in screen {
            try!(writeln!(writer, "{}", line));
        }

        try!(write!(writer, "<{}> ", label));
        if si == screen_count - 1 {
            try!(write!(writer, "<End of file> <Press any key to stop>"));
        } else {
            try!(write!(writer,
                        "<{}%> <Press any key for {} screen> <Press Escape to stop>",
                        ((si + 1) as f64 / screen_count as f64 * 100f64).round(),
                        if si + 1 == screen_count - 1 {
                            "last"
                        } else {
                            "next"
                        }));
        }
        try!(writer.flush());

        if input.getch().unwrap() == GETCH_ESC {
            break;
        }
    }

    try!(writeln!(writer));
    Ok(true)
}

/// Main context used to list a server.
pub struct ListContext {
    cururl: Url,
    files: Vec<RemoteFile>,
    selected: usize,
}

impl ListContext {
    /// Create a context, starting off at the provided URL.
    pub fn new(starting_url: Url) -> ListContext {
        ListContext {
            cururl: starting_url,
            files: vec![],
            selected: 0,
        }
    }

    /// Do one run of the input loop, effectively resulting in a single action.
    ///
    /// Returns `Ok(true)` to allow to continue next loop or `Ok(false)` to end listing.
    ///
    /// ### Commands
    ///
    /// Key|Result
    /// -------|-------
    /// Enter/Right Arrow | enter highlighted entry
    /// Escape/`'Q'`/`'q'` | end
    /// `'D'`/`'d'` | download file
    /// Up Arrow | move selection 1 entry up
    /// Down Arrow | move selection 1 entry down
    /// Left Arrow | go up one level, if not at root
    ///
    /// ### Entering entries
    ///
    /// If the entry is a directory, on the next loop the selected subdirectory wll be listed.
    ///
    /// If the entry is a UTF-8 file, its contents are paged, see [`paging_copy()`](fn.paging_copy.html).
    ///
    /// If the entry is a non-UTF-8 file, it is [downloaded](#downloading-files).
    ///
    /// ### Downloading files
    ///
    /// The user is shown a file picker and let choose where to download the file to,
    /// then the file is downloaded and saved to the specified location.
    ///
    /// The file isn't saved if the user cancels the picker.
    pub fn one_loop<W: Write>(&mut self, mut out: &mut W, input: &Getch, term_size: (usize, usize)) -> io::Result<bool> {
        try!(writeln!(out, "Contents of {}:", self.cururl));
        let mut resp = download(self.cururl.clone());
        if !resp.status().is_success() {
            try!(writeln!(out, "<Got {}...>", resp.status()));
            self.back();
            return Ok(true);
        }

        let data = match resp.json::<FilesetData>() {
            Ok(d) => d,
            Err(e) => {
                try!(writeln!(out, "<Couldn't parse server response: {}...>", e));
                self.back();
                return Ok(true);
            }
        };

        if data.is_file {
            if !try!(paging_copy(&mut download_raw(self.cururl.clone()), out, &self.cururl.path()[1..], &input, term_size)) {
                try!(writeln!(out, "<Not UTF-8, select download destination>"));
                try!(self.download_file(out, self.cururl.clone()));
            }
            self.cururl = parent_url(&self.cururl);
        } else {
            self.files = RemoteFile::from_response(data);
            {
                let mut tout = TabWriter::new(&mut out);
                for (i, f) in self.files.iter().enumerate() {
                    try!(writeln!(tout, "{}{}", if i == self.selected { ">" } else { " " }, f));
                }
                try!(tout.flush());
            }

            while let (true, exit) = try!(self.process_input(out, input)) {
                if exit {
                    return Ok(false);
                }
            }
        }

        Ok(true)
    }

    fn process_input<W: Write>(&mut self, out: &mut W, input: &Getch) -> io::Result<(bool, bool)> {
        match try!(input.getch()) {
            GETCH_ENTER => Ok((self.select(), false)),
            GETCH_ESC | b'q' | b'Q' => Ok((true, true)),
            b'd' | b'D' => {
                if !self.files.is_empty() {
                    try!(self.download_file(out, self.cururl.join(&self.files[self.selected].full_name).unwrap()));
                }
                Ok((self.files.is_empty(), false))
            }
            GETCH_ARROW_PREFIX => {
                match try!(input.getch()) {
                    GETCH_ARROW_UP => {
                        if self.selected != 0 {
                            try!(self.update_selected(out, ' '));
                            self.selected -= 1;
                            try!(self.update_selected(out, '>'));
                        }
                        Ok((true, false))
                    }
                    GETCH_ARROW_DOWN => {
                        try!(self.update_selected(out, ' '));
                        self.selected = cmp::min(self.selected + 1, self.files.len() - 1);
                        try!(self.update_selected(out, '>'));
                        Ok((true, false))
                    }
                    GETCH_ARROW_LEFT => Ok((self.back(), false)),
                    GETCH_ARROW_RIGHT => Ok((self.select(), false)),
                    _ => Ok((true, false)),
                }
            }
            _ => Ok((true, false)),
        }
    }

    fn update_selected<W: Write>(&self, out: &mut W, c: char) -> io::Result<()> {
        try!(write!(out, "{}", term::move_cursor_up(self.files.len() - self.selected)));
        try!(write!(out, "{}", c));
        try!(out.flush());
        try!(write!(out, "{}", term::move_cursor_back(1)));
        try!(write!(out, "{}", term::move_cursor_down(self.files.len() - self.selected)));
        Ok(())
    }

    fn select(&mut self) -> bool {
        if !self.files.is_empty() {
            self.cururl = self.cururl.join(&self.files[self.selected].full_name).unwrap();
            self.selected = 0;
        }
        self.files.is_empty()
    }

    fn back(&mut self) -> bool {
        let parent = parent_url(&self.cururl);
        if parent != self.cururl {
            self.cururl = parent;
            self.selected = 0;
            true
        } else {
            false
        }
    }

    fn download_file<W: Write>(&self, out: &mut W, u: Url) -> io::Result<()> {
        let f = PathBuf::from(&u.path()[1..]);
        if let Some(outp) = term::save_file_picker(f.file_name().unwrap(), f.extension()) {
            try!(writeln!(out, "<Downloading to {}...>", outp.display()));
            try!(io::copy(&mut download_raw(u), &mut try!(File::create(&outp))));
            try!(writeln!(out, "<Done!>"));
        }
        Ok(())
    }
}

/// Parsed `RawFileData` entry, prepared for end-user usage.
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct RemoteFile {
    /// Resource's name, ends with `'/'` if it's a directory.
    pub full_name: String,
    /// File size in bytes, `None` for directories.
    pub size: Option<u64>,
    /// Human-readable file size, `None` for directories.
    ///
    /// See [`util::human_readable_size()`](../util/fn.human_readable_size.html).
    pub human_size: Option<String>,
    /// Last modification time in local TZ, `None` for special `"../"` entry.
    pub last_modified: Option<Tm>,
}

impl RemoteFile {
    /// Parse a server-returned fileset into a set of user-viewable files.
    ///
    /// The returned vector is empty if the specified fileset describes a file.
    ///
    /// The returned vector contains an additional entry at the front with name `"../"` if the fileset isn't a root directory.
    ///
    /// The returned vector is partitioned according depending on directoriness status of the entry, directories first.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate rfsapi;
    /// # extern crate doh;
    /// # use doh::ops::RemoteFile;
    /// # use doh::util::parse_rfc3339;
    /// # use rfsapi::{FilesetData, RawFileData};
    /// # fn main() {
    /// assert_eq!(RemoteFile::from_response(FilesetData {
    ///     writes_supported: false,
    ///     is_root: false,
    ///     is_file: false,
    ///     files: vec![
    ///         RawFileData {
    ///             mime_type: "text/directory".parse().unwrap(),
    ///             name: "data".to_string(),
    ///             last_modified: parse_rfc3339("2012-02-22T14:53:18.42Z").unwrap(),
    ///             size: 0,
    ///             is_file: false,
    ///         },
    ///         RawFileData {
    ///             mime_type: "text/html".parse().unwrap(),
    ///             name: "index.html".to_string(),
    ///             last_modified: parse_rfc3339("2012-02-22T15:23:18Z").unwrap(),
    ///             size: 2297,
    ///             is_file: true,
    ///         },
    ///     ],
    /// }), vec![
    ///     RemoteFile {
    ///         full_name: "../".to_string(),
    ///         size: None,
    ///         human_size: None,
    ///         last_modified: None,
    ///     },
    ///     RemoteFile {
    ///         full_name: "data/".to_string(),
    ///         size: None,
    ///         human_size: None,
    ///         last_modified: Some(parse_rfc3339("2012-02-22T14:53:18.42Z").unwrap().to_local()),
    ///     },
    ///     RemoteFile {
    ///         full_name: "index.html".to_string(),
    ///         size: Some(2297),
    ///         human_size: Some("2.2KiB".to_string()),
    ///         last_modified: Some(parse_rfc3339("2012-02-22T15:23:18Z").unwrap().to_local()),
    ///     },
    /// ]);
    /// # }
    /// ```
    pub fn from_response(mut resp: FilesetData) -> Vec<RemoteFile> {
        if resp.is_file {
            vec![]
        } else {
            resp.files.sort_by_key(|f| f.is_file);
            let mut fs: Vec<_> = resp.files
                .into_iter()
                .map(From::from)
                .collect();
            if !resp.is_root {
                fs.insert(0,
                          RemoteFile {
                              full_name: "../".to_string(),
                              size: None,
                              human_size: None,
                              last_modified: None,
                          });
            }
            fs
        }
    }
}

impl From<RawFileData> for RemoteFile {
    fn from(rfd: RawFileData) -> RemoteFile {
        RemoteFile {
            full_name: rfd.name + if rfd.is_file { "" } else { "/" },
            size: if rfd.is_file { Some(rfd.size) } else { None },
            human_size: if rfd.is_file {
                Some(human_readable_size(rfd.size))
            } else {
                None
            },
            last_modified: Some(rfd.last_modified.to_local()),
        }
    }
}

/// Designed for use with `TabWriter`, separates entries with `\t`s.
impl fmt::Display for RemoteFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}\t", self.full_name));
        if let Some(ref hs) = self.human_size.as_ref() {
            try!(write!(f, "{}", hs));
        }
        try!(write!(f, "\t"));
        if let Some(ref lm) = self.last_modified.as_ref() {
            try!(write!(f, "{}", lm.rfc3339()));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:thread 'main' panicked at 'attempted to multiply with overflow'
// compile-flags: -C debug-assertions

fn main() {
    let _x = 2u32.pow(1024);
}
<commit_msg>Change the expected panic message for unsigned<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:thread 'main' panicked at 'attempt to multiply with overflow'
// compile-flags: -C debug-assertions

fn main() {
    let _x = 2u32.pow(1024);
}
<|endoftext|>


<commit_before>fn main() {
    // Make sure match uses the usual pointer comparison code path -- i.e., it should complain
    // that pointer comparison is disallowed, not that parts of a pointer are accessed as raw
    // bytes.
    let _: [u8; 0] = [4; { //~ ERROR could not evaluate repeat length
        match &1 as *const i32 as usize { //~ ERROR raw pointers cannot be cast to integers
            0 => 42, //~ ERROR constant contains unimplemented expression type
            //~^ NOTE "pointer arithmetic or comparison" needs an rfc before being allowed
            n => n,
        }
    }];
}
<commit_msg>add license text<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
    // Make sure match uses the usual pointer comparison code path -- i.e., it should complain
    // that pointer comparison is disallowed, not that parts of a pointer are accessed as raw
    // bytes.
    let _: [u8; 0] = [4; { //~ ERROR could not evaluate repeat length
        match &1 as *const i32 as usize { //~ ERROR raw pointers cannot be cast to integers
            0 => 42, //~ ERROR constant contains unimplemented expression type
            //~^ NOTE "pointer arithmetic or comparison" needs an rfc before being allowed
            n => n,
        }
    }];
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The ChaCha random number generator.

use core::prelude::*;
use core::num::Int;

use {Rng, SeedableRng, Rand};

const KEY_WORDS    : uint =  8; // 8 words for the 256-bit key
const STATE_WORDS  : uint = 16;
const CHACHA_ROUNDS: uint = 20; // Cryptographically secure from 8 upwards as of this writing

/// A random number generator that uses the ChaCha20 algorithm [1].
///
/// The ChaCha algorithm is widely accepted as suitable for
/// cryptographic purposes, but this implementation has not been
/// verified as such. Prefer a generator like `OsRng` that defers to
/// the operating system for cases that need high security.
///
/// [1]: D. J. Bernstein, [*ChaCha, a variant of
/// Salsa20*](http://cr.yp.to/chacha.html)

pub struct ChaChaRng {
    buffer:  [u32, ..STATE_WORDS], // Internal buffer of output
    state:   [u32, ..STATE_WORDS], // Initial state
    index:   uint,                 // Index into state
}

impl Copy for ChaChaRng {}

static EMPTY: ChaChaRng = ChaChaRng {
    buffer:  [0, ..STATE_WORDS],
    state:   [0, ..STATE_WORDS],
    index:   STATE_WORDS
};

macro_rules! quarter_round{
    ($a: expr, $b: expr, $c: expr, $d: expr) => {{
        $a += $b; $d ^= $a; $d = $d.rotate_left(16);
        $c += $d; $b ^= $c; $b = $b.rotate_left(12);
        $a += $b; $d ^= $a; $d = $d.rotate_left( 8);
        $c += $d; $b ^= $c; $b = $b.rotate_left( 7);
    }}
}

macro_rules! double_round{
    ($x: expr) => {{
        // Column round
        quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]);
        quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]);
        quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]);
        quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]);
        // Diagonal round
        quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]);
        quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]);
        quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]);
        quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]);
    }}
}

#[inline]
fn core(output: &mut [u32, ..STATE_WORDS], input: &[u32, ..STATE_WORDS]) {
    *output = *input;

    for _ in range(0, CHACHA_ROUNDS / 2) {
        double_round!(output);
    }

    for i in range(0, STATE_WORDS) {
        output[i] += input[i];
    }
}

impl ChaChaRng {

    /// Create an ChaCha random number generator using the default
    /// fixed key of 8 zero words.
    pub fn new_unseeded() -> ChaChaRng {
        let mut rng = EMPTY;
        rng.init(&[0, ..KEY_WORDS]);
        rng
    }

    /// Sets the internal 128-bit ChaCha counter to
    /// a user-provided value. This permits jumping
    /// arbitrarily ahead (or backwards) in the pseudorandom stream.
    ///
    /// Since the nonce words are used to extend the counter to 128 bits,
    /// users wishing to obtain the conventional ChaCha pseudorandom stream
    /// associated with a particular nonce can call this function with
    /// arguments `0, desired_nonce`.
    pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
        self.state[12] = (counter_low >>  0) as u32;
        self.state[13] = (counter_low >> 32) as u32;
        self.state[14] = (counter_high >>  0) as u32;
        self.state[15] = (counter_high >> 32) as u32;
        self.index = STATE_WORDS; // force recomputation
    }

    /// Initializes `self.state` with the appropriate key and constants
    ///
    /// We deviate slightly from the ChaCha specification regarding
    /// the nonce, which is used to extend the counter to 128 bits.
    /// This is provably as strong as the original cipher, though,
    /// since any distinguishing attack on our variant also works
    /// against ChaCha with a chosen-nonce. See the XSalsa20 [1]
    /// security proof for a more involved example of this.
    ///
    /// The modified word layout is:
    /// ```notrust
    /// constant constant constant constant
    /// key      key      key      key
    /// key      key      key      key
    /// counter  counter  counter  counter
    /// ```
    /// [1]: Daniel J. Bernstein. [*Extending the Salsa20
    /// nonce.*](http://cr.yp.to/papers.html#xsalsa)
    fn init(&mut self, key: &[u32, ..KEY_WORDS]) {
        self.state[0] = 0x61707865;
        self.state[1] = 0x3320646E;
        self.state[2] = 0x79622D32;
        self.state[3] = 0x6B206574;

        for i in range(0, KEY_WORDS) {
            self.state[4+i] = key[i];
        }

        self.state[12] = 0;
        self.state[13] = 0;
        self.state[14] = 0;
        self.state[15] = 0;

        self.index = STATE_WORDS;
    }

    /// Refill the internal output buffer (`self.buffer`)
    fn update(&mut self) {
        core(&mut self.buffer, &self.state);
        self.index = 0;
        // update 128-bit counter
        self.state[12] += 1;
        if self.state[12] != 0 { return };
        self.state[13] += 1;
        if self.state[13] != 0 { return };
        self.state[14] += 1;
        if self.state[14] != 0 { return };
        self.state[15] += 1;
    }
}

impl Rng for ChaChaRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        if self.index == STATE_WORDS {
            self.update();
        }

        let value = self.buffer[self.index % STATE_WORDS];
        self.index += 1;
        value
    }
}

impl<'a> SeedableRng<&'a [u32]> for ChaChaRng {

    fn reseed(&mut self, seed: &'a [u32]) {
        // reset state
        self.init(&[0u32, ..KEY_WORDS]);
        // set key in place
        let key = self.state.slice_mut(4, 4+KEY_WORDS);
        for (k, s) in key.iter_mut().zip(seed.iter()) {
            *k = *s;
        }
    }

    /// Create a ChaCha generator from a seed,
    /// obtained from a variable-length u32 array.
    /// Only up to 8 words are used; if less than 8
    /// words are used, the remaining are set to zero.
    fn from_seed(seed: &'a [u32]) -> ChaChaRng {
        let mut rng = EMPTY;
        rng.reseed(seed);
        rng
    }
}

impl Rand for ChaChaRng {
    fn rand<R: Rng>(other: &mut R) -> ChaChaRng {
        let mut key : [u32, ..KEY_WORDS] = [0, ..KEY_WORDS];
        for word in key.iter_mut() {
            *word = other.gen();
        }
        SeedableRng::from_seed(key.as_slice())
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use core::iter::order;
    use {Rng, SeedableRng};
    use super::ChaChaRng;

    #[test]
    fn test_rng_rand_seeded() {
        let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
        let mut ra: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        let mut rb: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        assert!(order::equals(ra.gen_ascii_chars().take(100),
                              rb.gen_ascii_chars().take(100)));
    }

    #[test]
    fn test_rng_seeded() {
        let seed : &[_] = &[0,1,2,3,4,5,6,7];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
        let mut rb: ChaChaRng = SeedableRng::from_seed(seed);
        assert!(order::equals(ra.gen_ascii_chars().take(100),
                              rb.gen_ascii_chars().take(100)));
    }

    #[test]
    fn test_rng_reseed() {
        let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
        let mut r: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        let string1: String = r.gen_ascii_chars().take(100).collect();

        r.reseed(s.as_slice());

        let string2: String = r.gen_ascii_chars().take(100).collect();
        assert_eq!(string1, string2);
    }

    #[test]
    fn test_rng_true_values() {
        // Test vectors 1 and 2 from
        // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
        let seed : &[_] = &[0u32, ..8];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);

        let v = Vec::from_fn(16, |_| ra.next_u32());
        assert_eq!(v,
                   vec!(0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653,
                        0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b,
                        0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8,
                        0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2));

        let v = Vec::from_fn(16, |_| ra.next_u32());
        assert_eq!(v,
                   vec!(0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73,
                        0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32,
                        0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874,
                        0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b));

        let seed : &[_] = &[0,1,2,3,4,5,6,7];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);

        // Store the 17*i-th 32-bit word,
        // i.e., the i-th word of the i-th 16-word block
        let mut v : Vec<u32> = Vec::new();
        for _ in range(0u, 16) {
            v.push(ra.next_u32());
            for _ in range(0u, 16) {
                ra.next_u32();
            }
        }

        assert_eq!(v,
                   vec!(0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036,
                        0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384,
                        0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530,
                        0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4));
    }
}

<commit_msg>rollup merge of #19614: steveklabnik/gh19599<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The ChaCha random number generator.

use core::prelude::*;
use core::num::Int;

use {Rng, SeedableRng, Rand};

const KEY_WORDS    : uint =  8; // 8 words for the 256-bit key
const STATE_WORDS  : uint = 16;
const CHACHA_ROUNDS: uint = 20; // Cryptographically secure from 8 upwards as of this writing

/// A random number generator that uses the ChaCha20 algorithm [1].
///
/// The ChaCha algorithm is widely accepted as suitable for
/// cryptographic purposes, but this implementation has not been
/// verified as such. Prefer a generator like `OsRng` that defers to
/// the operating system for cases that need high security.
///
/// [1]: D. J. Bernstein, [*ChaCha, a variant of
/// Salsa20*](http://cr.yp.to/chacha.html)

pub struct ChaChaRng {
    buffer:  [u32, ..STATE_WORDS], // Internal buffer of output
    state:   [u32, ..STATE_WORDS], // Initial state
    index:   uint,                 // Index into state
}

impl Copy for ChaChaRng {}

static EMPTY: ChaChaRng = ChaChaRng {
    buffer:  [0, ..STATE_WORDS],
    state:   [0, ..STATE_WORDS],
    index:   STATE_WORDS
};

macro_rules! quarter_round{
    ($a: expr, $b: expr, $c: expr, $d: expr) => {{
        $a += $b; $d ^= $a; $d = $d.rotate_left(16);
        $c += $d; $b ^= $c; $b = $b.rotate_left(12);
        $a += $b; $d ^= $a; $d = $d.rotate_left( 8);
        $c += $d; $b ^= $c; $b = $b.rotate_left( 7);
    }}
}

macro_rules! double_round{
    ($x: expr) => {{
        // Column round
        quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]);
        quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]);
        quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]);
        quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]);
        // Diagonal round
        quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]);
        quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]);
        quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]);
        quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]);
    }}
}

#[inline]
fn core(output: &mut [u32, ..STATE_WORDS], input: &[u32, ..STATE_WORDS]) {
    *output = *input;

    for _ in range(0, CHACHA_ROUNDS / 2) {
        double_round!(output);
    }

    for i in range(0, STATE_WORDS) {
        output[i] += input[i];
    }
}

impl ChaChaRng {

    /// Create an ChaCha random number generator using the default
    /// fixed key of 8 zero words.
    pub fn new_unseeded() -> ChaChaRng {
        let mut rng = EMPTY;
        rng.init(&[0, ..KEY_WORDS]);
        rng
    }

    /// Sets the internal 128-bit ChaCha counter to
    /// a user-provided value. This permits jumping
    /// arbitrarily ahead (or backwards) in the pseudorandom stream.
    ///
    /// Since the nonce words are used to extend the counter to 128 bits,
    /// users wishing to obtain the conventional ChaCha pseudorandom stream
    /// associated with a particular nonce can call this function with
    /// arguments `0, desired_nonce`.
    pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
        self.state[12] = (counter_low >>  0) as u32;
        self.state[13] = (counter_low >> 32) as u32;
        self.state[14] = (counter_high >>  0) as u32;
        self.state[15] = (counter_high >> 32) as u32;
        self.index = STATE_WORDS; // force recomputation
    }

    /// Initializes `self.state` with the appropriate key and constants
    ///
    /// We deviate slightly from the ChaCha specification regarding
    /// the nonce, which is used to extend the counter to 128 bits.
    /// This is provably as strong as the original cipher, though,
    /// since any distinguishing attack on our variant also works
    /// against ChaCha with a chosen-nonce. See the XSalsa20 [1]
    /// security proof for a more involved example of this.
    ///
    /// The modified word layout is:
    /// ```ignore
    /// constant constant constant constant
    /// key      key      key      key
    /// key      key      key      key
    /// counter  counter  counter  counter
    /// ```
    /// [1]: Daniel J. Bernstein. [*Extending the Salsa20
    /// nonce.*](http://cr.yp.to/papers.html#xsalsa)
    fn init(&mut self, key: &[u32, ..KEY_WORDS]) {
        self.state[0] = 0x61707865;
        self.state[1] = 0x3320646E;
        self.state[2] = 0x79622D32;
        self.state[3] = 0x6B206574;

        for i in range(0, KEY_WORDS) {
            self.state[4+i] = key[i];
        }

        self.state[12] = 0;
        self.state[13] = 0;
        self.state[14] = 0;
        self.state[15] = 0;

        self.index = STATE_WORDS;
    }

    /// Refill the internal output buffer (`self.buffer`)
    fn update(&mut self) {
        core(&mut self.buffer, &self.state);
        self.index = 0;
        // update 128-bit counter
        self.state[12] += 1;
        if self.state[12] != 0 { return };
        self.state[13] += 1;
        if self.state[13] != 0 { return };
        self.state[14] += 1;
        if self.state[14] != 0 { return };
        self.state[15] += 1;
    }
}

impl Rng for ChaChaRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        if self.index == STATE_WORDS {
            self.update();
        }

        let value = self.buffer[self.index % STATE_WORDS];
        self.index += 1;
        value
    }
}

impl<'a> SeedableRng<&'a [u32]> for ChaChaRng {

    fn reseed(&mut self, seed: &'a [u32]) {
        // reset state
        self.init(&[0u32, ..KEY_WORDS]);
        // set key in place
        let key = self.state.slice_mut(4, 4+KEY_WORDS);
        for (k, s) in key.iter_mut().zip(seed.iter()) {
            *k = *s;
        }
    }

    /// Create a ChaCha generator from a seed,
    /// obtained from a variable-length u32 array.
    /// Only up to 8 words are used; if less than 8
    /// words are used, the remaining are set to zero.
    fn from_seed(seed: &'a [u32]) -> ChaChaRng {
        let mut rng = EMPTY;
        rng.reseed(seed);
        rng
    }
}

impl Rand for ChaChaRng {
    fn rand<R: Rng>(other: &mut R) -> ChaChaRng {
        let mut key : [u32, ..KEY_WORDS] = [0, ..KEY_WORDS];
        for word in key.iter_mut() {
            *word = other.gen();
        }
        SeedableRng::from_seed(key.as_slice())
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use core::iter::order;
    use {Rng, SeedableRng};
    use super::ChaChaRng;

    #[test]
    fn test_rng_rand_seeded() {
        let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
        let mut ra: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        let mut rb: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        assert!(order::equals(ra.gen_ascii_chars().take(100),
                              rb.gen_ascii_chars().take(100)));
    }

    #[test]
    fn test_rng_seeded() {
        let seed : &[_] = &[0,1,2,3,4,5,6,7];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
        let mut rb: ChaChaRng = SeedableRng::from_seed(seed);
        assert!(order::equals(ra.gen_ascii_chars().take(100),
                              rb.gen_ascii_chars().take(100)));
    }

    #[test]
    fn test_rng_reseed() {
        let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
        let mut r: ChaChaRng = SeedableRng::from_seed(s.as_slice());
        let string1: String = r.gen_ascii_chars().take(100).collect();

        r.reseed(s.as_slice());

        let string2: String = r.gen_ascii_chars().take(100).collect();
        assert_eq!(string1, string2);
    }

    #[test]
    fn test_rng_true_values() {
        // Test vectors 1 and 2 from
        // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
        let seed : &[_] = &[0u32, ..8];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);

        let v = Vec::from_fn(16, |_| ra.next_u32());
        assert_eq!(v,
                   vec!(0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653,
                        0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b,
                        0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8,
                        0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2));

        let v = Vec::from_fn(16, |_| ra.next_u32());
        assert_eq!(v,
                   vec!(0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73,
                        0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32,
                        0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874,
                        0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b));

        let seed : &[_] = &[0,1,2,3,4,5,6,7];
        let mut ra: ChaChaRng = SeedableRng::from_seed(seed);

        // Store the 17*i-th 32-bit word,
        // i.e., the i-th word of the i-th 16-word block
        let mut v : Vec<u32> = Vec::new();
        for _ in range(0u, 16) {
            v.push(ra.next_u32());
            for _ in range(0u, 16) {
                ra.next_u32();
            }
        }

        assert_eq!(v,
                   vec!(0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036,
                        0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384,
                        0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530,
                        0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4));
    }
}

<|endoftext|>


<commit_before>use alloc::prelude::v1::*;
use hashbrown::HashSet;

use d7abi::fs::protocol::console::*;
use d7keymap::{KeyAction, KeyCodes, KeyMap, KeySymbol};

use crate::fs::{self, File};
use crate::syscall::SyscallResult;

pub struct Console {
    file: File,
    keycodes: KeyCodes,
    keymap: KeyMap,
    pressed_modifiers: HashSet<KeySymbol>,
}
impl Console {
    pub fn open(path: &str, keycodes_path: &str, keymap_path: &str) -> SyscallResult<Self> {
        Ok(Self {
            file: File::open(path)?,
            keycodes: serde_json::from_slice(&fs::read(keycodes_path)?)
                .expect("Invalid keycodes.json"),
            keymap: serde_json::from_slice(&fs::read(keymap_path)?).expect("Invalid keymap.json"),
            pressed_modifiers: HashSet::new(),
        })
    }

    fn process_event(&mut self, event: KeyboardEvent) -> EventAction {
        if let Some(keysym) = self.keycodes.clone().get(&event.keycode) {
            if self.keymap.modifiers.contains(&keysym) {
                if event.release {
                    self.pressed_modifiers.remove(keysym);
                } else {
                    self.pressed_modifiers.insert(keysym.clone());
                }
                EventAction::Ignore
            } else if event.release {
                let result = self.process_keysym_press(&keysym);
                if let Some(action) = result {
                    EventAction::KeyAction(action)
                } else {
                    EventAction::Unmatched(keysym.clone())
                }
            } else {
                EventAction::Ignore
            }
        } else {
            EventAction::NoSuchSymbol
        }
    }

    fn process_keysym_press(&mut self, keysym: &KeySymbol) -> Option<KeyAction> {
        for (k, v) in &self.keymap.mapping {
            if k.matches(keysym, &self.pressed_modifiers) {
                return Some(if let KeyAction::Remap(to) = v.clone() {
                    self.process_keysym_press(&to)?
                } else {
                    v.clone()
                });
            }
        }

        None
    }

    pub fn read_line(&mut self) -> SyscallResult<String> {
        use unicode_normalization::UnicodeNormalization;
        use unicode_segmentation::UnicodeSegmentation;

        let mut event_buffer = [0u8; 32];
        let mut result = String::new();
        let mut buffer = String::new();

        loop {
            self.file.read(&mut event_buffer)?;
            let event: KeyboardEvent =
                pinecone::from_bytes(&event_buffer).expect("Invalid key event");

            match self.process_event(event) {
                EventAction::KeyAction(action) => match action {
                    KeyAction::Text(text) => {
                        if !buffer.is_empty() {
                            result.extend(buffer.drain(..));
                        }
                        result.push_str(&text);
                        result = result.nfc().collect();
                    }
                    KeyAction::Buffer(text) => {
                        buffer.push_str(&text);
                    }
                    KeyAction::Remap(_) => unreachable!(),
                    KeyAction::Ignore => {}
                },
                EventAction::Unmatched(symbol) => match symbol.as_str() {
                    "Enter" => {
                        break;
                    }
                    "Backspace" => {
                        buffer.clear();
                        let mut c: Vec<_> =
                            UnicodeSegmentation::graphemes(result.as_str(), true).collect();
                        c.pop();
                        result = c.join("");
                    }
                    _ => {}
                },
                EventAction::Ignore | EventAction::NoSuchSymbol => {}
            }

            let msg: Vec<_> = pinecone::to_vec(&TextUpdate {
                line: result.clone(),
                newline: false,
            }).unwrap();
            let x = self.file.write(&msg)?;
            assert!(x == msg.len()); // TODO: write_all
        }

        let msg: Vec<_> = pinecone::to_vec(&TextUpdate {
            line: result.clone(),
            newline: true,
        }).unwrap();
        let x = self.file.write(&msg)?;
        assert!(x == msg.len()); // TODO: write_all

        Ok(result)
    }
}

#[must_use]
enum EventAction {
    KeyAction(KeyAction),
    Unmatched(KeySymbol),
    Ignore,
    NoSuchSymbol,
}
<commit_msg>Register typing on key press, not release<commit_after>use alloc::prelude::v1::*;
use hashbrown::HashSet;

use d7abi::fs::protocol::console::*;
use d7keymap::{KeyAction, KeyCodes, KeyMap, KeySymbol};

use crate::fs::{self, File};
use crate::syscall::SyscallResult;

pub struct Console {
    file: File,
    keycodes: KeyCodes,
    keymap: KeyMap,
    pressed_modifiers: HashSet<KeySymbol>,
}
impl Console {
    pub fn open(path: &str, keycodes_path: &str, keymap_path: &str) -> SyscallResult<Self> {
        Ok(Self {
            file: File::open(path)?,
            keycodes: serde_json::from_slice(&fs::read(keycodes_path)?)
                .expect("Invalid keycodes.json"),
            keymap: serde_json::from_slice(&fs::read(keymap_path)?).expect("Invalid keymap.json"),
            pressed_modifiers: HashSet::new(),
        })
    }

    fn process_event(&mut self, event: KeyboardEvent) -> EventAction {
        if let Some(keysym) = self.keycodes.clone().get(&event.keycode) {
            if self.keymap.modifiers.contains(&keysym) {
                if event.release {
                    self.pressed_modifiers.remove(keysym);
                } else {
                    self.pressed_modifiers.insert(keysym.clone());
                }
                EventAction::Ignore
            } else if !event.release {
                let result = self.process_keysym_press(&keysym);
                if let Some(action) = result {
                    EventAction::KeyAction(action)
                } else {
                    EventAction::Unmatched(keysym.clone())
                }
            } else {
                EventAction::Ignore
            }
        } else {
            EventAction::NoSuchSymbol
        }
    }

    fn process_keysym_press(&mut self, keysym: &KeySymbol) -> Option<KeyAction> {
        for (k, v) in &self.keymap.mapping {
            if k.matches(keysym, &self.pressed_modifiers) {
                return Some(if let KeyAction::Remap(to) = v.clone() {
                    self.process_keysym_press(&to)?
                } else {
                    v.clone()
                });
            }
        }

        None
    }

    pub fn read_line(&mut self) -> SyscallResult<String> {
        use unicode_normalization::UnicodeNormalization;
        use unicode_segmentation::UnicodeSegmentation;

        let mut event_buffer = [0u8; 32];
        let mut result = String::new();
        let mut buffer = String::new();

        loop {
            self.file.read(&mut event_buffer)?;
            let event: KeyboardEvent =
                pinecone::from_bytes(&event_buffer).expect("Invalid key event");

            match self.process_event(event) {
                EventAction::KeyAction(action) => match action {
                    KeyAction::Text(text) => {
                        if !buffer.is_empty() {
                            result.extend(buffer.drain(..));
                        }
                        result.push_str(&text);
                        result = result.nfc().collect();
                    }
                    KeyAction::Buffer(text) => {
                        buffer.push_str(&text);
                    }
                    KeyAction::Remap(_) => unreachable!(),
                    KeyAction::Ignore => {}
                },
                EventAction::Unmatched(symbol) => match symbol.as_str() {
                    "Enter" => {
                        break;
                    }
                    "Backspace" => {
                        buffer.clear();
                        let mut c: Vec<_> =
                            UnicodeSegmentation::graphemes(result.as_str(), true).collect();
                        c.pop();
                        result = c.join("");
                    }
                    _ => {}
                },
                EventAction::Ignore | EventAction::NoSuchSymbol => {}
            }

            let msg: Vec<_> = pinecone::to_vec(&TextUpdate {
                line: result.clone(),
                newline: false,
            }).unwrap();
            let x = self.file.write(&msg)?;
            assert!(x == msg.len()); // TODO: write_all
        }

        let msg: Vec<_> = pinecone::to_vec(&TextUpdate {
            line: result.clone(),
            newline: true,
        }).unwrap();
        let x = self.file.write(&msg)?;
        assert!(x == msg.len()); // TODO: write_all

        Ok(result)
    }
}

#[must_use]
enum EventAction {
    KeyAction(KeyAction),
    Unmatched(KeySymbol),
    Ignore,
    NoSuchSymbol,
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.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 ffipredict;
use tle;

use std::default::Default;
use time;

#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub lat_deg: f64,
    pub lon_deg: f64,
    pub alt_m: i32,
}

#[derive(Default, Debug)]
pub struct Sat {
    /// next AOS
    pub aos:                Option<time::Tm>,

    /// next LOS
    pub los:                Option<time::Tm>,

    /// azimuth [deg]
    pub az_deg:             f64,

    /// elevation [deg]
    pub el_deg:             f64,

    /// range [km]
    pub range_km:           f64,

    /// range rate [km/sec]
    pub range_rate_km_sec:  f64,

    /// SSP latitude [deg]
    pub lat_deg:            f64,

    /// SSP longitude [deg]
    pub lon_deg:            f64,

    /// altitude [km]
    pub alt_km:             f64,

    /// velocity [km/s]
    pub vel_km_s:           f64,

    /// orbit number
    pub orbit_nr:           u64,
}

#[derive(Debug)]
pub struct Predict {
    pub sat: Sat,

    p_sat: ffipredict::sat_t,
    p_qth: ffipredict::qth_t,
}

fn fraction_of_day(h: i32, m: i32, s: i32) -> f64{
    (h as f64 + (m as f64 + s as f64 / 60.0) / 60.0) / 24.0
}

/// Astronomical Formulae for Calculators, Jean Meeus, pages 23-25.
/// Calculate Julian Date of 0.0 Jan year
fn julian_date_of_year(yr: i32) -> f64 {
    let mut year: u64;
    let mut a: f64;
    let mut b: f64;
    let mut i: f64;

    let mut jdoy: f64;

    year = yr as u64 -1;
    i = (year as f64 / 100.).trunc();
    a = i;
    i = (a / 4.).trunc();
    b = (2. - a + i).trunc();
    i = (365.25 * year as f64).trunc();
    i += (30.6001_f64 * 14.0_f64).trunc();
    jdoy = i + 1720994.5 + b;

    jdoy
}

/// Calculates the day of the year for the specified date.
fn day_of_the_year(yr: i32, mo: i32, dy: i32) -> i32 {
    let days: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let mut day: i32 = 0;

    for d in &days[0 .. mo as usize - 1] {
        day += *d as i32;
    }

    day += dy as i32;
    if (yr % 4 == 0) && ((yr % 100 != 0) || (yr % 400 == 0)) && (mo > 2) {
        day += 1;
    }

    day
}

// Calculates Julian Day Number
fn julian_timestamp(t: time::Tm) -> f64 {
    let year = t.tm_year + 1900;
    let month = t.tm_mon + 1;

    julian_date_of_year(year) +
        day_of_the_year(year, month, t.tm_mday) as f64 +
        fraction_of_day(t.tm_hour, t.tm_min, t.tm_sec) +
        t.tm_nsec as f64 / 1000_f64 / 8.64e+10
}

pub fn julian_to_unix(julian: f64) -> time::Tm {
    let unix = (julian - 2440587.5) * 86400.;
    let t = time::Timespec::new(unix.trunc() as i64, unix.fract() as i32);
    time::at(t)
}

impl Predict {

    pub fn new(tle: &tle::Tle, location: &Location) -> Predict {
        let tle_t = tle::create_tle_t(tle).unwrap();

        let sgps: ffipredict::sgpsdp_static_t = Default::default();
        let dps: ffipredict::deep_static_t = Default::default();
        let deep_arg: ffipredict::deep_arg_t = Default::default();
        let pos: ffipredict::vector_t = Default::default();
        let vel: ffipredict::vector_t = Default::default();

        let mut sat_t = ffipredict::sat_t{
            name: b"placeholder\0".as_ptr() as *const i8,
            nickname: b"placeholder\0".as_ptr() as *const i8,
            website: b"placeholder\0".as_ptr() as *const i8,
            tle: tle_t,
            flags: 0,
            sgps: sgps,
            dps: dps,
            deep_arg: deep_arg,
            pos: pos,
            vel: vel,

            jul_epoch: 0.0,
            jul_utc: 0.0,
            tsince: 0.0,
            aos: 0.0,
            los: 0.0,
            az: 0.0,
            el: 0.0,
            range: 0.0,
            range_rate: 0.0,
            ra: 0.0,
            dec: 0.0,
            ssplat: 0.0,
            ssplon: 0.0,
            alt: 0.0,
            velo: 0.0,
            ma: 0.0,
            footprint: 0.0,
            phase: 0.0,
            meanmo: 0.0,
            orbit: 0,
            otype: ffipredict::orbit_type_t::ORBIT_TYPE_UNKNOWN,
        };

        let sat: Sat = Default::default();
        let mut qth = ffipredict::qth_t {
            name: b"placeholder\0".as_ptr() as *const i8,
            loc: b"placeholder\0".as_ptr() as *const i8,
            desc: b"placeholder\0".as_ptr() as *const i8,
            lat: location.lat_deg,
            lon: location.lon_deg,
            alt: location.alt_m,
            qra: b"placeholder\0".as_ptr() as *const i8,
            wx: b"placeholder\0".as_ptr() as *const i8,
        };

        unsafe {ffipredict::select_ephemeris(&mut sat_t)};
        unsafe {ffipredict::gtk_sat_data_init_sat(&mut sat_t, &mut qth)};

        Predict{sat: sat, p_sat: sat_t, p_qth: qth}
    }

    pub fn update(&mut self, timeoption: Option<time::Tm>) {
        let juliantime = match timeoption {
            Some(t) => julian_timestamp(t),
            None => unsafe {ffipredict::get_current_daynum()}
        };

        // we do not have AOS with some satellites, therefore option is used
        let aos = match unsafe {ffipredict::find_aos(&mut self.p_sat, &mut self.p_qth, juliantime, 1.0)} {
            0.0 => None,
            aos => Some(julian_to_unix(aos)),
        };
        let los = match unsafe {ffipredict::find_los(&mut self.p_sat, &mut self.p_qth, juliantime, 1.0)} {
            0.0 => None,
            los => Some(julian_to_unix(los)),
        };

        unsafe {ffipredict::predict_calc(&mut self.p_sat, &mut self.p_qth, juliantime)};

        self.sat.aos                = aos;
        self.sat.los                = los;
        self.sat.az_deg             = self.p_sat.az;
        self.sat.el_deg             = self.p_sat.el;
        self.sat.range_km           = self.p_sat.range;
        self.sat.range_rate_km_sec  = self.p_sat.range_rate;
        self.sat.lat_deg            = self.p_sat.ssplat;
        self.sat.lon_deg            = self.p_sat.ssplon;
        self.sat.alt_km             = self.p_sat.alt;
        self.sat.vel_km_s           = self.p_sat.velo;
        self.sat.orbit_nr           = self.p_sat.orbit as u64;
    }
}

#[test]
fn test_julian_timestamp() {
    // http://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number
    let t = time::strptime("2000-1-1 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
    assert_eq!(julian_timestamp(t), 2451545.0);

    let t = time::strptime("1970-1-1 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
    assert_eq!(julian_timestamp(t), 2440587.5);
}
<commit_msg>fix Location PartialEq: ignore differences smaller than 1e-6 in lat and lon<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.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 ffipredict;
use tle;

use std::default::Default;
use time;

#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
pub struct Location {
    pub lat_deg: f64,
    pub lon_deg: f64,
    pub alt_m: i32,
}

impl PartialEq for Location {
    fn eq(&self, other: &Location) -> bool {
        self.alt_m == other.alt_m &&
        (self.lat_deg - other.lat_deg).abs() < 0.000_000_1 &&
        (self.lon_deg - other.lon_deg).abs() < 0.000_000_1
    }
}

#[test]
fn location_partialeq() {
    let first  = Location { lat_deg: 56.7865,            lon_deg: 21.4444,            alt_m: 8 };
    let second = Location { lat_deg: 56.786500000000004, lon_deg: 21.444399999999998, alt_m: 8 };
    assert_eq!(first, second);
}

#[derive(Default, Debug)]
pub struct Sat {
    /// next AOS
    pub aos:                Option<time::Tm>,

    /// next LOS
    pub los:                Option<time::Tm>,

    /// azimuth [deg]
    pub az_deg:             f64,

    /// elevation [deg]
    pub el_deg:             f64,

    /// range [km]
    pub range_km:           f64,

    /// range rate [km/sec]
    pub range_rate_km_sec:  f64,

    /// SSP latitude [deg]
    pub lat_deg:            f64,

    /// SSP longitude [deg]
    pub lon_deg:            f64,

    /// altitude [km]
    pub alt_km:             f64,

    /// velocity [km/s]
    pub vel_km_s:           f64,

    /// orbit number
    pub orbit_nr:           u64,
}

#[derive(Debug)]
pub struct Predict {
    pub sat: Sat,

    p_sat: ffipredict::sat_t,
    p_qth: ffipredict::qth_t,
}

fn fraction_of_day(h: i32, m: i32, s: i32) -> f64{
    (h as f64 + (m as f64 + s as f64 / 60.0) / 60.0) / 24.0
}

/// Astronomical Formulae for Calculators, Jean Meeus, pages 23-25.
/// Calculate Julian Date of 0.0 Jan year
fn julian_date_of_year(yr: i32) -> f64 {
    let mut year: u64;
    let mut a: f64;
    let mut b: f64;
    let mut i: f64;

    let mut jdoy: f64;

    year = yr as u64 -1;
    i = (year as f64 / 100.).trunc();
    a = i;
    i = (a / 4.).trunc();
    b = (2. - a + i).trunc();
    i = (365.25 * year as f64).trunc();
    i += (30.6001_f64 * 14.0_f64).trunc();
    jdoy = i + 1720994.5 + b;

    jdoy
}

/// Calculates the day of the year for the specified date.
fn day_of_the_year(yr: i32, mo: i32, dy: i32) -> i32 {
    let days: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let mut day: i32 = 0;

    for d in &days[0 .. mo as usize - 1] {
        day += *d as i32;
    }

    day += dy as i32;
    if (yr % 4 == 0) && ((yr % 100 != 0) || (yr % 400 == 0)) && (mo > 2) {
        day += 1;
    }

    day
}

// Calculates Julian Day Number
fn julian_timestamp(t: time::Tm) -> f64 {
    let year = t.tm_year + 1900;
    let month = t.tm_mon + 1;

    julian_date_of_year(year) +
        day_of_the_year(year, month, t.tm_mday) as f64 +
        fraction_of_day(t.tm_hour, t.tm_min, t.tm_sec) +
        t.tm_nsec as f64 / 1000_f64 / 8.64e+10
}

pub fn julian_to_unix(julian: f64) -> time::Tm {
    let unix = (julian - 2440587.5) * 86400.;
    let t = time::Timespec::new(unix.trunc() as i64, unix.fract() as i32);
    time::at(t)
}

impl Predict {

    pub fn new(tle: &tle::Tle, location: &Location) -> Predict {
        let tle_t = tle::create_tle_t(tle).unwrap();

        let sgps: ffipredict::sgpsdp_static_t = Default::default();
        let dps: ffipredict::deep_static_t = Default::default();
        let deep_arg: ffipredict::deep_arg_t = Default::default();
        let pos: ffipredict::vector_t = Default::default();
        let vel: ffipredict::vector_t = Default::default();

        let mut sat_t = ffipredict::sat_t{
            name: b"placeholder\0".as_ptr() as *const i8,
            nickname: b"placeholder\0".as_ptr() as *const i8,
            website: b"placeholder\0".as_ptr() as *const i8,
            tle: tle_t,
            flags: 0,
            sgps: sgps,
            dps: dps,
            deep_arg: deep_arg,
            pos: pos,
            vel: vel,

            jul_epoch: 0.0,
            jul_utc: 0.0,
            tsince: 0.0,
            aos: 0.0,
            los: 0.0,
            az: 0.0,
            el: 0.0,
            range: 0.0,
            range_rate: 0.0,
            ra: 0.0,
            dec: 0.0,
            ssplat: 0.0,
            ssplon: 0.0,
            alt: 0.0,
            velo: 0.0,
            ma: 0.0,
            footprint: 0.0,
            phase: 0.0,
            meanmo: 0.0,
            orbit: 0,
            otype: ffipredict::orbit_type_t::ORBIT_TYPE_UNKNOWN,
        };

        let sat: Sat = Default::default();
        let mut qth = ffipredict::qth_t {
            name: b"placeholder\0".as_ptr() as *const i8,
            loc: b"placeholder\0".as_ptr() as *const i8,
            desc: b"placeholder\0".as_ptr() as *const i8,
            lat: location.lat_deg,
            lon: location.lon_deg,
            alt: location.alt_m,
            qra: b"placeholder\0".as_ptr() as *const i8,
            wx: b"placeholder\0".as_ptr() as *const i8,
        };

        unsafe {ffipredict::select_ephemeris(&mut sat_t)};
        unsafe {ffipredict::gtk_sat_data_init_sat(&mut sat_t, &mut qth)};

        Predict{sat: sat, p_sat: sat_t, p_qth: qth}
    }

    pub fn update(&mut self, timeoption: Option<time::Tm>) {
        let juliantime = match timeoption {
            Some(t) => julian_timestamp(t),
            None => unsafe {ffipredict::get_current_daynum()}
        };

        // we do not have AOS with some satellites, therefore option is used
        let aos = match unsafe {ffipredict::find_aos(&mut self.p_sat, &mut self.p_qth, juliantime, 1.0)} {
            0.0 => None,
            aos => Some(julian_to_unix(aos)),
        };
        let los = match unsafe {ffipredict::find_los(&mut self.p_sat, &mut self.p_qth, juliantime, 1.0)} {
            0.0 => None,
            los => Some(julian_to_unix(los)),
        };

        unsafe {ffipredict::predict_calc(&mut self.p_sat, &mut self.p_qth, juliantime)};

        self.sat.aos                = aos;
        self.sat.los                = los;
        self.sat.az_deg             = self.p_sat.az;
        self.sat.el_deg             = self.p_sat.el;
        self.sat.range_km           = self.p_sat.range;
        self.sat.range_rate_km_sec  = self.p_sat.range_rate;
        self.sat.lat_deg            = self.p_sat.ssplat;
        self.sat.lon_deg            = self.p_sat.ssplon;
        self.sat.alt_km             = self.p_sat.alt;
        self.sat.vel_km_s           = self.p_sat.velo;
        self.sat.orbit_nr           = self.p_sat.orbit as u64;
    }
}

#[test]
fn test_julian_timestamp() {
    // http://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number
    let t = time::strptime("2000-1-1 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
    assert_eq!(julian_timestamp(t), 2451545.0);

    let t = time::strptime("1970-1-1 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
    assert_eq!(julian_timestamp(t), 2440587.5);
}
<|endoftext|>


<commit_before>// Copyright 2015 Bobby Powers. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//extern crate libc;
//use libc::geteuid;

use std::fs::{self, File};
use std::io::{Error, ErrorKind, Read, Result};

const PROC_PATH: &'static str = "/proc";

struct CmdStat {
    name: String,
    pss: f32,
    shared: f32,
    heap: f32,
    swap: f32,
}

#[inline(never)]
fn is_digit(d: char) -> bool {
    d >= '0' && d <= '9'
}

#[inline(never)]
fn first_char(name: &std::ffi::OsStr) -> char {
    use std::os::unix::ffi::OsStrExt;
    name.as_bytes()[0] as char
}

fn is_dir(md: fs::Metadata) -> Result<fs::Metadata> {
    if md.is_dir() {
        Ok(md)
    } else {
        Err(Error::new(ErrorKind::NotFound, "not a directory"))
    }
}

#[inline(never)]
fn get_pids() -> Result<Vec<i32>> {
    let dir = fs::read_dir(PROC_PATH)?;

    // TODO: look at filter_map
    let pids: Vec<i32> = dir
        .filter(|e| e.is_ok()) // ignore bad dirent results
        .map(|e| e.unwrap()) // make this a list of dirents
        .filter(|e| is_digit(first_char(&e.file_name())))
        .filter(|e| fs::metadata(e.path()).and_then(|md| is_dir(md)).is_ok())
        .map(|e| i32::from_str_radix(e.file_name().to_str().unwrap(), 10).unwrap())
        .collect();

    Ok(pids)
}

fn proc_cmdline(pid: i32) -> Result<String> {
    let mut buf = String::new();
    let path = format!("/proc/{}/cmdline", pid);

    // TODO: we don't really care about reading all of the
    // cmdline, just the first 1024 chars is enough.
    let mut f = File::open(path)?;

    f.read_to_string(&mut buf)?;

    Ok(buf)
}

fn proc_name(pid: i32) -> Result<String> {
    let path = format!("/proc/{}/exe", pid);

    let owned_path = fs::read_link(path)?;
    let full_path = owned_path.to_string_lossy();
    let cmdline = proc_cmdline(pid)?;

    let result = if cmdline.starts_with(&*full_path) {
        // the basename (last component) of the path
        owned_path
            .file_name()
            .unwrap()
            .to_string_lossy()
            .to_string()
    } else {
        cmdline
    };

    Ok(result)
}

fn cmdstat_new(pid: i32) -> Option<CmdStat> {
    match proc_name(pid) {
        Ok(name) => Some(CmdStat {
            name: name,
            pss: 0.0,
            shared: 0.0,
            heap: 0.0,
            swap: 0.0,
        }),
        Err(_) => None,
    }
}

fn cmdstats_for(pids: Vec<i32>) -> Vec<CmdStat> {
    pids.into_iter().filter_map(cmdstat_new).collect()
}

fn main() {
    // command line flag parsing

    // check euid
    // unsafe {
    // 	let uid = geteuid();
    // 	if uid != 0 {
    // 	    println!("we aren't root");
    // 	}
    // }

    let pids_r = get_pids();
    if let Err(err) = pids_r {
        println!("get_pids: {}", err);
        return;
    }

    let pids = pids_r.unwrap();
    // get details of all pids (possibly in parallel)
    let infos = cmdstats_for(pids);

    // sort pid details

    // find total

    // print_results
}
<commit_msg>rust: sort stats<commit_after>// Copyright 2015 Bobby Powers. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//extern crate libc;
//use libc::geteuid;

use std::cmp::{Eq, Ordering};
use std::fs::{self, File};
use std::io::{Error, ErrorKind, Read, Result};

const PROC_PATH: &'static str = "/proc";

struct CmdStat {
    name: String,
    pss: f32,
    shared: f32,
    heap: f32,
    swap: f32,
}

impl PartialEq for CmdStat {
    fn eq(&self, other: &CmdStat) -> bool {
        self.name == other.name
    }
}
impl Eq for CmdStat {}

impl PartialOrd for CmdStat {
    fn partial_cmp(&self, other: &CmdStat) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for CmdStat {
    fn cmp(&self, other: &CmdStat) -> Ordering {
        self.name.cmp(&other.name)
    }
}

#[inline(never)]
fn is_digit(d: char) -> bool {
    d >= '0' && d <= '9'
}

#[inline(never)]
fn first_char(name: &std::ffi::OsStr) -> char {
    use std::os::unix::ffi::OsStrExt;
    name.as_bytes()[0] as char
}

fn is_dir(md: fs::Metadata) -> Result<fs::Metadata> {
    if md.is_dir() {
        Ok(md)
    } else {
        Err(Error::new(ErrorKind::NotFound, "not a directory"))
    }
}

#[inline(never)]
fn get_pids() -> Result<Vec<i32>> {
    let dir = fs::read_dir(PROC_PATH)?;

    // TODO: look at filter_map
    let pids: Vec<i32> = dir
        .filter(|e| e.is_ok()) // ignore bad dirent results
        .map(|e| e.unwrap()) // make this a list of dirents
        .filter(|e| is_digit(first_char(&e.file_name())))
        .filter(|e| fs::metadata(e.path()).and_then(|md| is_dir(md)).is_ok())
        .map(|e| i32::from_str_radix(e.file_name().to_str().unwrap(), 10).unwrap())
        .collect();

    Ok(pids)
}

fn proc_cmdline(pid: i32) -> Result<String> {
    let mut buf = String::new();
    let path = format!("/proc/{}/cmdline", pid);

    // TODO: we don't really care about reading all of the
    // cmdline, just the first 1024 chars is enough.
    let mut f = File::open(path)?;

    f.read_to_string(&mut buf)?;

    Ok(buf)
}

fn proc_name(pid: i32) -> Result<String> {
    let path = format!("/proc/{}/exe", pid);

    let owned_path = fs::read_link(path)?;
    let full_path = owned_path.to_string_lossy();
    let cmdline = proc_cmdline(pid)?;

    let result = if cmdline.starts_with(&*full_path) {
        // the basename (last component) of the path
        owned_path
            .file_name()
            .unwrap()
            .to_string_lossy()
            .to_string()
    } else {
        cmdline
    };

    Ok(result)
}

fn cmdstat_new(pid: i32) -> Option<CmdStat> {
    match proc_name(pid) {
        Ok(name) => Some(CmdStat {
            name: name,
            pss: 0.0,
            shared: 0.0,
            heap: 0.0,
            swap: 0.0,
        }),
        Err(_) => None,
    }
}

fn cmdstats_for(pids: Vec<i32>) -> Vec<CmdStat> {
    pids.into_iter().filter_map(cmdstat_new).collect()
}

fn main() {
    // command line flag parsing

    // check euid
    // unsafe {
    // 	let uid = geteuid();
    // 	if uid != 0 {
    // 	    println!("we aren't root");
    // 	}
    // }

    let pids_r = get_pids();
    if let Err(err) = pids_r {
        println!("get_pids: {}", err);
        return;
    }

    let pids = pids_r.unwrap();

    let mut stats = cmdstats_for(pids);

    stats.sort_unstable();

    // merge adjacent/find total

    // print_results
}
<|endoftext|>