<commit_before>use std::io::{Read, Write};
use std::fmt;
use std::cell::{RefMut, RefCell};
use std::ascii::AsciiExt;

use serde::{Serialize, Deserialize};
use serde_json;
use curl;
use url;

use error::GGRResult;
use error::GGRError;

fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                      out: &mut W,
                      body: Option<Vec<u8>>)
                      -> GGRResult<(u32, Vec<String>)> {
    match body {
        Some(body) => {
            let mut body = &body[..];
            handle.upload(true)?;
            handle.in_filesize(body.len() as u64)?;
            handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
        }
        None => handle_req(handle, out, &mut |_| 0),
    }
}

fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                        out: &mut W,
                        read: &mut FnMut(&mut [u8]) -> usize)
                        -> GGRResult<(u32, Vec<String>)> {
    let mut headers = Vec::new();
    {
        let mut handle = handle.transfer();
        handle.read_function(|buf| Ok(read(buf)))?;
        handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })?;
        handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })?;
        handle.perform()?;
    }

    Ok((handle.response_code()?, headers))
}

#[derive(PartialEq, Debug)]
enum CallMethod {
    Get,
    Post,
    Put,
    Delete,
}

impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
            CallMethod::Post => write!(f, "POST"),
            CallMethod::Put => write!(f, "PUT"),
            CallMethod::Delete => write!(f, "DELETE"),
        }
    }
}

pub struct Call {
    shared_handle: RefCell<curl::easy::Easy>,
    base: url::Url,
}

impl Call {
    pub fn new(url: &url::Url) -> Call {
        Call {
            shared_handle: RefCell::new(curl::easy::Easy::new()),
            base: url.clone(),
        }
    }

    pub fn set_url_query(&mut self, q: Option<&str>) {
        self.base.set_query(q);
    }

    // Low Level Methods

    fn do_request(&self, method: &CallMethod, url: &str) -> GGRResult<CallRequest> {
        let mut handle = self.shared_handle.borrow_mut();
        try!(handle.cookie_session(true));
        try!(handle.netrc(curl::easy::NetRc::Required));

        CallRequest::new(handle, method, url)
    }

    fn request<S: Serialize>(&self, method: CallMethod, path: &str, body: Option<&S>) -> GGRResult<CallResponse> {
        let mut sendurl = self.base.clone();
        // double replace for pathes with three ///.
        let complete_path = format!("{}/{}", sendurl.path(), path).replace("//", "/").replace("//", "/");
        sendurl.set_path(&complete_path);

        debug!("url-to-send: {:?}", sendurl);

        for am in vec!(
            curl::easy::Auth::new().digest(true),
            curl::easy::Auth::new().basic(true),
        ) {
            let mut call_request = try!(self.do_request(&method, &sendurl.to_owned().into_string()));
            if let Some(body) = body {
                call_request.with_json_body(&body).ok();
            }

            try!(call_request.handle.http_auth(am));
            let call_response = try!(call_request.send());
            if call_response.status == 401 {
                continue;
            }
            return Ok(call_response);
        }
        Err(GGRError::General("No Authentication algorithm found for your gerrit server. 'basic' and 'digest' tested".into()))
    }

    /// Convenience method that performs a `GET` request.
    pub fn get(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Get, path, None)
    }

    /// Convenience method that performs a `DELETE` request.
    pub fn delete(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Delete, path, None)
    }

    /// Convenience method that performs a `POST` request with JSON data.
    pub fn post<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Post, path, Some(body))
    }

    /// Convenience method that performs a `PUT` request with JSON data.
    pub fn put<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Put, path, Some(body))
    }
}

/// Iterator over response headers
#[allow(dead_code)]
pub struct Headers<'a> {
    lines: &'a [String],
    idx: usize,
}

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

    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.lines.get(self.idx).map(|line| {
            self.idx += 1;
            match line.find(':') {
                Some(i) => (&line[..i], line[i + 1..].trim()),
                None => (line[..].trim(), ""),
            }
        })
    }
}

pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    fn new(mut handle: RefMut<'a, curl::easy::Easy>,
           method: &CallMethod,
           url: &str)
           -> GGRResult<CallRequest<'a>> {
        info!("request {} {}", method, url);

        let mut headers = curl::easy::List::new();
        headers.append("Accept: application/json").ok();

        match *method {
            CallMethod::Get => try!(handle.get(true)),
            CallMethod::Post => try!(handle.custom_request("POST")),
            CallMethod::Put => try!(handle.custom_request("PUT")),
            CallMethod::Delete => try!(handle.custom_request("DELETE")),
        }

        handle.url(url)?;

        Ok(CallRequest {
            handle: handle,
            headers: headers,
            body: None,
        })
    }

    /// adds a specific header to the request
    pub fn with_header(mut self, key: &str, value: &str) -> GGRResult<CallRequest<'a>> {
        self.headers.append(&format!("{}: {}", key, value))?;
        Ok(self)
    }

    /// sets the JSON request body for the request.
    pub fn with_json_body<S: Serialize>(&mut self, body: &S) -> GGRResult<&mut CallRequest<'a>> {
        let mut body_bytes: Vec<u8> = vec![];
        serde_json::to_writer(&mut body_bytes, &body)?;
        info!("sending JSON data ({} bytes)", body_bytes.len());
        self.body = Some(body_bytes);
        self.headers.append("Content-Type: application/json")?;
        Ok(self)
    }

    /// attaches some form data to the request.
    pub fn with_form_data(&mut self, form: curl::easy::Form) -> GGRResult<&mut CallRequest<'a>> {
        info!("sending form data");
        self.handle.httppost(form)?;
        self.body = None;
        Ok(self)
    }

    /// enables or disables redirects.  The default is off.
    pub fn follow_location(&mut self, val: bool) -> GGRResult<&mut CallRequest<'a>> {
        info!("follow redirects: {}", val);
        self.handle.follow_location(val)?;
        Ok(self)
    }

    /// Sends the request and writes response data into the given file
    /// instead of the response object's in memory buffer.
    pub fn send_into<W: Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        self.handle.http_headers(self.headers)?;
        let local_body = self.body.clone();
        let (status, headers) = send_req(&mut self.handle, out, local_body)?;
        info!("response: {}", status);
        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Sends the request and reads the response body into the response object.
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = vec![];
        let mut rv = self.send_into(&mut out)?;

        debug!("return-from-server: {:?}", rv);

        // cut first 4 bytes from output stream
        // *NOTICE**: The first 4 characters are cutted from the returned content. We want only
        // json data which has a prevention against XSSI attacks. More here:
        // <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
        if out.starts_with(b")]}'") {
            out = out[4..].into();
        }

        rv.body = Some(out);
        Ok(rv)
    }
}

#[derive(Clone, Debug)]
pub struct CallResponse {
    status: u32,
    headers: Vec<String>,
    body: Option<Vec<u8>>,
}

impl CallResponse {
    /// Returns the status code of the response
    pub fn status(&self) -> u32 {
        self.status
    }

    /// Indicates that the request failed
    pub fn failed(&self) -> bool {
        self.status >= 400 && self.status <= 600
    }

    /// Indicates that the request succeeded
    pub fn ok(&self) -> bool {
        !self.failed()
    }

    /// Converts the response into a result object.  This also converts non okay response codes
    /// into errors.
    pub fn to_result(&self) -> GGRResult<&CallResponse> {
        debug!("headers:");
        for (header_key, header_value) in self.headers() {
            if !header_value.is_empty() {
                debug!("  {}: {}", header_key, header_value);
            }
        }
        if let Some(ref body) = self.body {
            debug!("body: {}", String::from_utf8_lossy(body));
        }
        if self.ok() {
            return Ok(self);
        }
        Err(GGRError::General(format!("generic error: {}", self.status())))
    }

    /// Deserializes the response body into the given type
    pub fn deserialize<T: Deserialize>(&self) -> GGRResult<T> {
        Ok(serde_json::from_reader(match self.body {
            Some(ref body) => body,
            None => &b""[..],
        })?)
    }

    /// Like `deserialize` but consumes the response and will convert
    /// failed requests into proper errors.
    pub fn convert<T: Deserialize>(self) -> GGRResult<T> {
        self.to_result().and_then(|x| x.deserialize())
    }

    /// Iterates over the headers.
    #[allow(dead_code)]
    pub fn headers(&self) -> Headers {
        Headers {
            lines: &self.headers[..],
            idx: 0,
        }
    }

    /// Looks up the first matching header for a key.
    #[allow(dead_code)]
    pub fn get_header(&self, key: &str) -> Option<&str> {
        for (header_key, header_value) in self.headers() {
            if header_key.eq_ignore_ascii_case(key) {
                return Some(header_value);
            }
        }
        None
    }

    pub fn get_body(&self) -> Option<Vec<u8>> {
        self.body.clone()
    }
}
<commit_msg>put debugoutput to debugchannel<commit_after>use std::io::{Read, Write};
use std::fmt;
use std::cell::{RefMut, RefCell};
use std::ascii::AsciiExt;

use serde::{Serialize, Deserialize};
use serde_json;
use curl;
use url;

use error::GGRResult;
use error::GGRError;

fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                      out: &mut W,
                      body: Option<Vec<u8>>)
                      -> GGRResult<(u32, Vec<String>)> {
    match body {
        Some(body) => {
            let mut body = &body[..];
            handle.upload(true)?;
            handle.in_filesize(body.len() as u64)?;
            handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
        }
        None => handle_req(handle, out, &mut |_| 0),
    }
}

fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                        out: &mut W,
                        read: &mut FnMut(&mut [u8]) -> usize)
                        -> GGRResult<(u32, Vec<String>)> {
    let mut headers = Vec::new();
    {
        let mut handle = handle.transfer();
        handle.read_function(|buf| Ok(read(buf)))?;
        handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })?;
        handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })?;
        handle.perform()?;
    }

    Ok((handle.response_code()?, headers))
}

#[derive(PartialEq, Debug)]
enum CallMethod {
    Get,
    Post,
    Put,
    Delete,
}

impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
            CallMethod::Post => write!(f, "POST"),
            CallMethod::Put => write!(f, "PUT"),
            CallMethod::Delete => write!(f, "DELETE"),
        }
    }
}

pub struct Call {
    shared_handle: RefCell<curl::easy::Easy>,
    base: url::Url,
}

impl Call {
    pub fn new(url: &url::Url) -> Call {
        Call {
            shared_handle: RefCell::new(curl::easy::Easy::new()),
            base: url.clone(),
        }
    }

    pub fn set_url_query(&mut self, q: Option<&str>) {
        self.base.set_query(q);
    }

    // Low Level Methods

    fn do_request(&self, method: &CallMethod, url: &str) -> GGRResult<CallRequest> {
        let mut handle = self.shared_handle.borrow_mut();
        try!(handle.cookie_session(true));
        try!(handle.netrc(curl::easy::NetRc::Required));

        CallRequest::new(handle, method, url)
    }

    fn request<S: Serialize>(&self, method: CallMethod, path: &str, body: Option<&S>) -> GGRResult<CallResponse> {
        let mut sendurl = self.base.clone();
        // double replace for pathes with three ///.
        let complete_path = format!("{}/{}", sendurl.path(), path).replace("//", "/").replace("//", "/");
        sendurl.set_path(&complete_path);

        debug!("url-to-send: {:?}", sendurl);

        for am in vec!(
            curl::easy::Auth::new().digest(true),
            curl::easy::Auth::new().basic(true),
        ) {
            let mut call_request = try!(self.do_request(&method, &sendurl.to_owned().into_string()));
            if let Some(body) = body {
                call_request.with_json_body(&body).ok();
            }

            try!(call_request.handle.http_auth(am));
            let call_response = try!(call_request.send());
            if call_response.status == 401 {
                continue;
            }
            return Ok(call_response);
        }
        Err(GGRError::General("No Authentication algorithm found for your gerrit server. 'basic' and 'digest' tested".into()))
    }

    /// Convenience method that performs a `GET` request.
    pub fn get(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Get, path, None)
    }

    /// Convenience method that performs a `DELETE` request.
    pub fn delete(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Delete, path, None)
    }

    /// Convenience method that performs a `POST` request with JSON data.
    pub fn post<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Post, path, Some(body))
    }

    /// Convenience method that performs a `PUT` request with JSON data.
    pub fn put<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Put, path, Some(body))
    }
}

/// Iterator over response headers
#[allow(dead_code)]
pub struct Headers<'a> {
    lines: &'a [String],
    idx: usize,
}

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

    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.lines.get(self.idx).map(|line| {
            self.idx += 1;
            match line.find(':') {
                Some(i) => (&line[..i], line[i + 1..].trim()),
                None => (line[..].trim(), ""),
            }
        })
    }
}

pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    fn new(mut handle: RefMut<'a, curl::easy::Easy>,
           method: &CallMethod,
           url: &str)
           -> GGRResult<CallRequest<'a>> {
        debug!("request {} {}", method, url);

        let mut headers = curl::easy::List::new();
        headers.append("Accept: application/json").ok();

        match *method {
            CallMethod::Get => try!(handle.get(true)),
            CallMethod::Post => try!(handle.custom_request("POST")),
            CallMethod::Put => try!(handle.custom_request("PUT")),
            CallMethod::Delete => try!(handle.custom_request("DELETE")),
        }

        handle.url(url)?;

        Ok(CallRequest {
            handle: handle,
            headers: headers,
            body: None,
        })
    }

    /// adds a specific header to the request
    pub fn with_header(mut self, key: &str, value: &str) -> GGRResult<CallRequest<'a>> {
        self.headers.append(&format!("{}: {}", key, value))?;
        Ok(self)
    }

    /// sets the JSON request body for the request.
    pub fn with_json_body<S: Serialize>(&mut self, body: &S) -> GGRResult<&mut CallRequest<'a>> {
        let mut body_bytes: Vec<u8> = vec![];
        serde_json::to_writer(&mut body_bytes, &body)?;
        debug!("sending JSON data ({} bytes)", body_bytes.len());
        self.body = Some(body_bytes);
        self.headers.append("Content-Type: application/json")?;
        Ok(self)
    }

    /// attaches some form data to the request.
    pub fn with_form_data(&mut self, form: curl::easy::Form) -> GGRResult<&mut CallRequest<'a>> {
        debug!("sending form data");
        self.handle.httppost(form)?;
        self.body = None;
        Ok(self)
    }

    /// enables or disables redirects.  The default is off.
    pub fn follow_location(&mut self, val: bool) -> GGRResult<&mut CallRequest<'a>> {
        debug!("follow redirects: {}", val);
        self.handle.follow_location(val)?;
        Ok(self)
    }

    /// Sends the request and writes response data into the given file
    /// instead of the response object's in memory buffer.
    pub fn send_into<W: Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        self.handle.http_headers(self.headers)?;
        let local_body = self.body.clone();
        let (status, headers) = send_req(&mut self.handle, out, local_body)?;
        debug!("response: {}", status);
        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Sends the request and reads the response body into the response object.
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = vec![];
        let mut rv = self.send_into(&mut out)?;

        debug!("return-from-server: {:?}", rv);

        // cut first 4 bytes from output stream
        // *NOTICE**: The first 4 characters are cutted from the returned content. We want only
        // json data which has a prevention against XSSI attacks. More here:
        // <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
        if out.starts_with(b")]}'") {
            out = out[4..].into();
        }

        rv.body = Some(out);
        Ok(rv)
    }
}

#[derive(Clone, Debug)]
pub struct CallResponse {
    status: u32,
    headers: Vec<String>,
    body: Option<Vec<u8>>,
}

impl CallResponse {
    /// Returns the status code of the response
    pub fn status(&self) -> u32 {
        self.status
    }

    /// Indicates that the request failed
    pub fn failed(&self) -> bool {
        self.status >= 400 && self.status <= 600
    }

    /// Indicates that the request succeeded
    pub fn ok(&self) -> bool {
        !self.failed()
    }

    /// Converts the response into a result object.  This also converts non okay response codes
    /// into errors.
    pub fn to_result(&self) -> GGRResult<&CallResponse> {
        debug!("headers:");
        for (header_key, header_value) in self.headers() {
            if !header_value.is_empty() {
                debug!("  {}: {}", header_key, header_value);
            }
        }
        if let Some(ref body) = self.body {
            debug!("body: {}", String::from_utf8_lossy(body));
        }
        if self.ok() {
            return Ok(self);
        }
        Err(GGRError::General(format!("generic error: {}", self.status())))
    }

    /// Deserializes the response body into the given type
    pub fn deserialize<T: Deserialize>(&self) -> GGRResult<T> {
        Ok(serde_json::from_reader(match self.body {
            Some(ref body) => body,
            None => &b""[..],
        })?)
    }

    /// Like `deserialize` but consumes the response and will convert
    /// failed requests into proper errors.
    pub fn convert<T: Deserialize>(self) -> GGRResult<T> {
        self.to_result().and_then(|x| x.deserialize())
    }

    /// Iterates over the headers.
    #[allow(dead_code)]
    pub fn headers(&self) -> Headers {
        Headers {
            lines: &self.headers[..],
            idx: 0,
        }
    }

    /// Looks up the first matching header for a key.
    #[allow(dead_code)]
    pub fn get_header(&self, key: &str) -> Option<&str> {
        for (header_key, header_value) in self.headers() {
            if header_key.eq_ignore_ascii_case(key) {
                return Some(header_value);
            }
        }
        None
    }

    pub fn get_body(&self) -> Option<Vec<u8>> {
        self.body.clone()
    }
}
<|endoftext|>


<commit_before>use events::Milliseconds;
use node::State;
use storage::View;

/// Timeout value used in the `Default` implementation for the `Constant` timeout adjuster.
const DEFAULT_CONSTANT_TIMEOUT: Milliseconds = 200;

/// `TimeoutAdjuster` trait can be used to dynamically change propose timeout.
///
/// # Examples
///
/// Implementing `TimeoutAdjuster`:
///
/// ```
/// use exonum::node::State;
/// use exonum::node::timeout_adjuster::TimeoutAdjuster;
/// use exonum::events::Milliseconds;
/// use exonum::storage::View;
///
/// # #[allow(dead_code)]
/// struct CustomAdjuster {}
///
/// impl TimeoutAdjuster for CustomAdjuster {
///     fn adjust_timeout(&mut self, state: &State, _: View) -> Milliseconds {
///         // Simply increase propose time after empty blocks.
///         if state.transactions().is_empty() {
///             1000
///         } else {
///             200
///         }
///     }
/// }
/// ```
/// For more examples see `Constant` and `MovingAverage` implementations.
pub trait TimeoutAdjuster {
    /// Called during node initialization and after accepting a new height.
    fn adjust_timeout(&mut self, state: &State, view: View) -> Milliseconds;
}

/// `Adjuster` implementation that always returns the same value.
pub struct Constant {
    timeout: Milliseconds,
}

impl Constant {
    /// Creates `Constant` with given timeout value.
    pub fn new(timeout: Milliseconds) -> Self {
        Constant { timeout: timeout }
    }
}

impl Default for Constant {
    fn default() -> Self {
        Constant{ timeout: DEFAULT_CONSTANT_TIMEOUT }
    }
}

impl TimeoutAdjuster for Constant {
    fn adjust_timeout(&mut self, _: &State, _: View) -> Milliseconds {
        self.timeout
    }
}

/// Moving average timeout calculation. Initial timeout is equal to `min_timeout`.
pub struct MovingAverage {
    adjustment_speed: f64,
    optimal_block_load: f64,
    min_timeout: f64,
    max_timeout: f64,
    previous_timeout: f64,
}

impl MovingAverage {
    /// Creates `MovingAverage` that generates values between `min_timeout` and `max_timeout`.
    pub fn new(adjustment_speed: f64,
               optimal_block_load: f64,
               min_timeout: Milliseconds,
               max_timeout: Milliseconds) -> Self {
        MovingAverage {
            adjustment_speed: adjustment_speed,
            optimal_block_load: optimal_block_load,
            min_timeout: min_timeout as f64,
            max_timeout: max_timeout as f64,
            previous_timeout: min_timeout as f64,
        }
    }

    fn adjust_timeout_impl(&mut self,
                           txs_block_limit: f64,
                           current_load: f64) -> Milliseconds {
        let optimal_load = txs_block_limit * self.optimal_block_load;
        let load_percent = current_load / optimal_load;

        let target_t = if current_load < optimal_load {
            self.max_timeout - (self.max_timeout - self.previous_timeout) * load_percent
        } else {
            self.previous_timeout - (self.previous_timeout - self.min_timeout) *
                (load_percent - 1.) / (1. / self.optimal_block_load - 1.)
        };

        self.previous_timeout = target_t * self.adjustment_speed + self.previous_timeout *
            (1. - self.adjustment_speed);
        self.previous_timeout.round() as Milliseconds
    }
}

impl TimeoutAdjuster for MovingAverage {
    fn adjust_timeout(&mut self, state: &State, _: View) -> Milliseconds {
        self.adjust_timeout_impl(state.config().consensus.txs_block_limit as f64,
                                 state.transactions().len() as f64)
    }
}

#[cfg(test)]
mod tests {
    use env_logger;

    use super::*;
    use events::Milliseconds;

    #[test]
    fn moving_average_timeout_adjuster() {
        let _ = env_logger::init();

        static MIN_TIMEOUT: Milliseconds = 1;
        static MAX_TIMEOUT: Milliseconds = 10000;
        static TXS_BLOCK_LIMIT: f64 = 5000.;
        static TEST_COUNT: usize = 10;

        let mut adjuster = MovingAverage::new(0.75, 0.7, MIN_TIMEOUT, MAX_TIMEOUT);

        // Timeout should stay minimal if there are `TXS_BLOCK_LIMIT` or more transactions.
        for _ in 0..TEST_COUNT {
            assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT));
        }

        for _ in 0..TEST_COUNT {
            assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT,
                                                                 TXS_BLOCK_LIMIT * 2.));
        }

        static TXS_TEST_DATA: &'static [f64] = &[0., 100., 200., 300., 400., 500., 1000., 1500.,
            2000., 2500., 3000., 4000., 4500., 5000., 5500., 6000.];

        // As the transaction number declines, timeout should increase until it reaches maximum.
        let mut previous_timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT);
        for transactions in TXS_TEST_DATA.iter().rev() {
            let timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, *transactions);
            info!("Timeout: current = {}, previous = {}", timeout, previous_timeout);
            assert!(timeout >= previous_timeout);
            previous_timeout = timeout;
        }

        // Timeout should stay maximal if there are no transactions for some time.
        for _ in 0..TEST_COUNT {
            assert_eq!(MAX_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, 0.));
        }

        // As the transactions number increases, timeout should decrease until it reaches minimum.
        let mut previous_timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, 0.);
        for transactions in TXS_TEST_DATA {
            let timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, *transactions);
            info!("Timeout: current = {}, previous = {}", timeout, previous_timeout);
            assert!(timeout <= previous_timeout);
            previous_timeout = timeout;
        }
        assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT));
    }
}
<commit_msg>Implement Send for TimeoutAdjuster<commit_after>use events::Milliseconds;
use node::State;
use storage::View;

/// Timeout value used in the `Default` implementation for the `Constant` timeout adjuster.
const DEFAULT_CONSTANT_TIMEOUT: Milliseconds = 200;

/// `TimeoutAdjuster` trait can be used to dynamically change propose timeout.
///
/// # Examples
///
/// Implementing `TimeoutAdjuster`:
///
/// ```
/// use exonum::node::State;
/// use exonum::node::timeout_adjuster::TimeoutAdjuster;
/// use exonum::events::Milliseconds;
/// use exonum::storage::View;
///
/// # #[allow(dead_code)]
/// struct CustomAdjuster {}
///
/// impl TimeoutAdjuster for CustomAdjuster {
///     fn adjust_timeout(&mut self, state: &State, _: View) -> Milliseconds {
///         // Simply increase propose time after empty blocks.
///         if state.transactions().is_empty() {
///             1000
///         } else {
///             200
///         }
///     }
/// }
/// ```
/// For more examples see `Constant` and `MovingAverage` implementations.
pub trait TimeoutAdjuster: Send {
    /// Called during node initialization and after accepting a new height.
    fn adjust_timeout(&mut self, state: &State, view: View) -> Milliseconds;
}

/// `Adjuster` implementation that always returns the same value.
pub struct Constant {
    timeout: Milliseconds,
}

impl Constant {
    /// Creates `Constant` with given timeout value.
    pub fn new(timeout: Milliseconds) -> Self {
        Constant { timeout: timeout }
    }
}

impl Default for Constant {
    fn default() -> Self {
        Constant{ timeout: DEFAULT_CONSTANT_TIMEOUT }
    }
}

impl TimeoutAdjuster for Constant {
    fn adjust_timeout(&mut self, _: &State, _: View) -> Milliseconds {
        self.timeout
    }
}

/// Moving average timeout calculation. Initial timeout is equal to `min_timeout`.
pub struct MovingAverage {
    adjustment_speed: f64,
    optimal_block_load: f64,
    min_timeout: f64,
    max_timeout: f64,
    previous_timeout: f64,
}

impl MovingAverage {
    /// Creates `MovingAverage` that generates values between `min_timeout` and `max_timeout`.
    pub fn new(adjustment_speed: f64,
               optimal_block_load: f64,
               min_timeout: Milliseconds,
               max_timeout: Milliseconds) -> Self {
        MovingAverage {
            adjustment_speed: adjustment_speed,
            optimal_block_load: optimal_block_load,
            min_timeout: min_timeout as f64,
            max_timeout: max_timeout as f64,
            previous_timeout: min_timeout as f64,
        }
    }

    fn adjust_timeout_impl(&mut self,
                           txs_block_limit: f64,
                           current_load: f64) -> Milliseconds {
        let optimal_load = txs_block_limit * self.optimal_block_load;
        let load_percent = current_load / optimal_load;

        let target_t = if current_load < optimal_load {
            self.max_timeout - (self.max_timeout - self.previous_timeout) * load_percent
        } else {
            self.previous_timeout - (self.previous_timeout - self.min_timeout) *
                (load_percent - 1.) / (1. / self.optimal_block_load - 1.)
        };

        self.previous_timeout = target_t * self.adjustment_speed + self.previous_timeout *
            (1. - self.adjustment_speed);
        self.previous_timeout.round() as Milliseconds
    }
}

impl TimeoutAdjuster for MovingAverage {
    fn adjust_timeout(&mut self, state: &State, _: View) -> Milliseconds {
        self.adjust_timeout_impl(state.config().consensus.txs_block_limit as f64,
                                 state.transactions().len() as f64)
    }
}

#[cfg(test)]
mod tests {
    use env_logger;

    use super::*;
    use events::Milliseconds;

    #[test]
    fn moving_average_timeout_adjuster() {
        let _ = env_logger::init();

        static MIN_TIMEOUT: Milliseconds = 1;
        static MAX_TIMEOUT: Milliseconds = 10000;
        static TXS_BLOCK_LIMIT: f64 = 5000.;
        static TEST_COUNT: usize = 10;

        let mut adjuster = MovingAverage::new(0.75, 0.7, MIN_TIMEOUT, MAX_TIMEOUT);

        // Timeout should stay minimal if there are `TXS_BLOCK_LIMIT` or more transactions.
        for _ in 0..TEST_COUNT {
            assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT));
        }

        for _ in 0..TEST_COUNT {
            assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT,
                                                                 TXS_BLOCK_LIMIT * 2.));
        }

        static TXS_TEST_DATA: &'static [f64] = &[0., 100., 200., 300., 400., 500., 1000., 1500.,
            2000., 2500., 3000., 4000., 4500., 5000., 5500., 6000.];

        // As the transaction number declines, timeout should increase until it reaches maximum.
        let mut previous_timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT);
        for transactions in TXS_TEST_DATA.iter().rev() {
            let timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, *transactions);
            info!("Timeout: current = {}, previous = {}", timeout, previous_timeout);
            assert!(timeout >= previous_timeout);
            previous_timeout = timeout;
        }

        // Timeout should stay maximal if there are no transactions for some time.
        for _ in 0..TEST_COUNT {
            assert_eq!(MAX_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, 0.));
        }

        // As the transactions number increases, timeout should decrease until it reaches minimum.
        let mut previous_timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, 0.);
        for transactions in TXS_TEST_DATA {
            let timeout = adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, *transactions);
            info!("Timeout: current = {}, previous = {}", timeout, previous_timeout);
            assert!(timeout <= previous_timeout);
            previous_timeout = timeout;
        }
        assert_eq!(MIN_TIMEOUT, adjuster.adjust_timeout_impl(TXS_BLOCK_LIMIT, TXS_BLOCK_LIMIT));
    }
}
<|endoftext|>


<commit_before>//
// gash.rs
// by Nathan Typanski
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::io::{stdin, stdio, File, Truncate, Write};
use std::io::process::ProcessExit;
use std::io::signal::{Listener, Interrupt};
use std::path::posix::Path;
use std::option::Option;
use std::task::try;
use std::run::Process;
use std::run::ProcessOptions;
use std::comm::Port;

use extra::getopts;

use std::libc::types::os::arch::posix88::pid_t;
use std::libc;

extern {
  pub fn kill(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int;
}

// The basic unit for a command that could be run.
struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

struct FgProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl FgProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<FgProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(FgProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
struct BgProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    pid          : Option<i32>,
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl BgProcess {
    fn new(cmd : Cmd) -> Option<BgProcess> {
        if (cmd_exists(&cmd)) {
            Some(BgProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Process ports; these don't leave this function and are used for
        // sending the PID out in the return value.
        let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    // Send the pid out for the return value
                    pidchan.try_send_deferred(Some(process.get_id()));
                    chan.try_send_deferred(process.finish());
                }
                None => {
                    pidchan.try_send_deferred(None);
                }
            }
        });
        self.exit_port = Some(port);
        self.pid = pidport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BgProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        // Setup the interrupt handler. Has to happen here, or it won't
        // retain control over interrupts.
        let mut listener = Listener::new();
        listener.register(Interrupt);
        let port = listener.port;
        spawn(proc() {
            try(proc() {
                loop {
                    match port.recv_opt() {
                        Some(Interrupt) => {
                        }
                        None => {
                            break;
                        }
                        _ => {
                        }
                    }
                }
                return 0
            });
            return ();
        });

        // Standard input reader
        let mut stdin = BufferedReader::new(stdin());
        loop {
            // Show the prompt
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.disown_dead();

            // Push commands onto the history
            match program {
                "" => {}
                _  => {
                    self.push_hist(cmd_line)
                }
            }

            match program {
                "" =>  {
                    continue;
                }
                "exit" =>  {
                    self.kill_all();
                    return;
                }
                "history" => {
                    self.show_hist();
                }
                "jobs" => {
                    self.jobs();
                }
                "cd" =>  {
                    self.chdir(cmd_line);
                }
                _ => {
                    self.run_cmdline(cmd_line);
                }
            }
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
                Some(pid) => {
                    println!(" {:i}", pid);
                }
                None => {
                    println("");
                }
            }
        }
    }

    // Remove dead processes from the list of processes.
    fn disown_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            borrowed_maybe(&cmd.exit_port, |port| match port.try_recv() {
                Some(_) => {dead.push(i); Some(i)},
                _ => None,
            });
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.pid {
                Some(pid) => {
                    unsafe {
                        // Kill perhaps isn't the best way to do this, but
                        // without it we have no way of killing our background
                        // processes with only the PID - we'd need the process
                        // object itself, and that can't be moved out of the
                        // spawn() that it's trapped inside.
                        kill(pid, 15);
                    }
                }
                None => {
                }
            }
        }
    }

    // Push a new command onto history.
    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    // Determine the type of the current block, and send it to the right
    // parsing function.
    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    // Parse a new lone process. Background/foreground it appropriately.
    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        maybe(Cmd::new(cmd_line), |cmd| {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        })
    }

    // Parse a process pipeline, and redirect stdin/stdout appropriately.
    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        maybe(Cmd::new(pair[0].trim()), |cmd| {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                maybe(make_process(cmd, None, None), |process| Some(~process))
            }
        })
    }

    // Redirect the stdout of Process two into the stdin of `cmd`, and return
    // the process created from `cmd`.
    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
            Some(mut process) => {
                match two {
                    Some(mut p2) => {
                        let output = p2.finish_with_output();
                        if output.status.success() {
                            process.input().write(output.output);
                            process.close_input();
                            process.finish();
                        }
                        Some(~process)
                    }
                    None => {
                        println("No two remain.");
                        Some(~process)
                    }
                }
            }
            None => {
                None
            }
        }
    }

    // Make a new background process, and push it onto our stack of tracked
    // background processes.
    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BgProcess::new(cmd) {
            Some(mut process) => {
                match process.run() {
                    Some(pid) => {
                        println!("{:s} {:i}", name, pid);
                        self.processes.push(~process);
                    }
                    None => {
                    }
                }
            }
            None => {
            }
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
            Some(cmd_str) => {cmd_str.to_owned()},
            None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

// Determine whether a command exists using "which".
fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

// Returns true if a (presuambly background) process has finished running.
fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
        Some(ref p) => {
            match p.try_recv() {
                Some(_) => {
                    dead = true;
                },
                None => {
                }
            }
        }
        _ => {
        }
    }
    dead
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    maybe(FgProcess::new(cmd, stdin, stdout),
          |mut cmdprocess| cmdprocess.run())
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match maybe(Cmd::new(pair[1].trim()), |c| make_process(c, None, Some(1))) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
                Some(file) => {
                    let proc_input = process.input();
                    let mut file_buffer = BufferedReader::new(file);
                    proc_input.write(file_buffer.read_to_end());
                }
                None => {
                }
            }
        }
        None => {
        }
    }
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    let cmd = Cmd::new(pair[1].trim());
    match maybe(cmd, |c| make_process(c, None, None)) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }
    }
}

// Describes a computation that could fail.
// If v is Some(...), then f is called on v and the result is returned.
// Otherwise, None is returned.
fn maybe<A, B>(v : Option<A>, f : |A| -> Option<B>) -> Option<B> {
    match v {
        Some(v) => f(v),
        None    => None,
    }
}

// Same as maybe(v, f), but for &v.
fn borrowed_maybe<A, B>(v : &Option<A>, f : |&A| -> Option<B>) -> Option<B> {
    match *v {
        Some(ref v) => f(v),
        None    => None,
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => {
            let mut shell = Shell::new("");
            shell.run_cmdline(cmd_line);
        }
        None => {
            let mut shell = Shell::new("gash > ");
            shell.run();
        }
    }
}
<commit_msg>remove unused is_dead()<commit_after>//
// gash.rs
// by Nathan Typanski
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::io::{stdin, stdio, File, Truncate, Write};
use std::io::process::ProcessExit;
use std::io::signal::{Listener, Interrupt};
use std::path::posix::Path;
use std::option::Option;
use std::task::try;
use std::run::Process;
use std::run::ProcessOptions;
use std::comm::Port;

use extra::getopts;

use std::libc::types::os::arch::posix88::pid_t;
use std::libc;

extern {
  pub fn kill(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int;
}

// The basic unit for a command that could be run.
struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

struct FgProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl FgProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<FgProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(FgProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
struct BgProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    pid          : Option<i32>,
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl BgProcess {
    fn new(cmd : Cmd) -> Option<BgProcess> {
        if (cmd_exists(&cmd)) {
            Some(BgProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Process ports; these don't leave this function and are used for
        // sending the PID out in the return value.
        let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    // Send the pid out for the return value
                    pidchan.try_send_deferred(Some(process.get_id()));
                    chan.try_send_deferred(process.finish());
                }
                None => {
                    pidchan.try_send_deferred(None);
                }
            }
        });
        self.exit_port = Some(port);
        self.pid = pidport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BgProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        // Setup the interrupt handler. Has to happen here, or it won't
        // retain control over interrupts.
        let mut listener = Listener::new();
        listener.register(Interrupt);
        let port = listener.port;
        spawn(proc() {
            try(proc() {
                loop {
                    match port.recv_opt() {
                        Some(Interrupt) => {
                        }
                        None => {
                            break;
                        }
                        _ => {
                        }
                    }
                }
                return 0
            });
            return ();
        });

        // Standard input reader
        let mut stdin = BufferedReader::new(stdin());
        loop {
            // Show the prompt
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.disown_dead();

            // Push commands onto the history
            match program {
                "" => {}
                _  => {
                    self.push_hist(cmd_line)
                }
            }

            match program {
                "" =>  {
                    continue;
                }
                "exit" =>  {
                    self.kill_all();
                    return;
                }
                "history" => {
                    self.show_hist();
                }
                "jobs" => {
                    self.jobs();
                }
                "cd" =>  {
                    self.chdir(cmd_line);
                }
                _ => {
                    self.run_cmdline(cmd_line);
                }
            }
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
                Some(pid) => {
                    println!(" {:i}", pid);
                }
                None => {
                    println("");
                }
            }
        }
    }

    // Remove dead processes from the list of processes.
    fn disown_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            borrowed_maybe(&cmd.exit_port, |port| match port.try_recv() {
                Some(_) => {dead.push(i); Some(i)},
                _ => None,
            });
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.pid {
                Some(pid) => {
                    unsafe {
                        // Kill perhaps isn't the best way to do this, but
                        // without it we have no way of killing our background
                        // processes with only the PID - we'd need the process
                        // object itself, and that can't be moved out of the
                        // spawn() that it's trapped inside.
                        kill(pid, 15);
                    }
                }
                None => {
                }
            }
        }
    }

    // Push a new command onto history.
    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    // Determine the type of the current block, and send it to the right
    // parsing function.
    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    // Parse a new lone process. Background/foreground it appropriately.
    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        maybe(Cmd::new(cmd_line), |cmd| {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        })
    }

    // Parse a process pipeline, and redirect stdin/stdout appropriately.
    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        maybe(Cmd::new(pair[0].trim()), |cmd| {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                maybe(make_process(cmd, None, None), |process| Some(~process))
            }
        })
    }

    // Redirect the stdout of Process two into the stdin of `cmd`, and return
    // the process created from `cmd`.
    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
            Some(mut process) => {
                match two {
                    Some(mut p2) => {
                        let output = p2.finish_with_output();
                        if output.status.success() {
                            process.input().write(output.output);
                            process.close_input();
                            process.finish();
                        }
                        Some(~process)
                    }
                    None => {
                        println("No two remain.");
                        Some(~process)
                    }
                }
            }
            None => {
                None
            }
        }
    }

    // Make a new background process, and push it onto our stack of tracked
    // background processes.
    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BgProcess::new(cmd) {
            Some(mut process) => {
                match process.run() {
                    Some(pid) => {
                        println!("{:s} {:i}", name, pid);
                        self.processes.push(~process);
                    }
                    None => {
                    }
                }
            }
            None => {
            }
        }
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
            Some(cmd_str) => {cmd_str.to_owned()},
            None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

// Determine whether a command exists using "which".
fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    maybe(FgProcess::new(cmd, stdin, stdout),
          |mut cmdprocess| cmdprocess.run())
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match maybe(Cmd::new(pair[1].trim()), |c| make_process(c, None, Some(1))) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
                Some(file) => {
                    let proc_input = process.input();
                    let mut file_buffer = BufferedReader::new(file);
                    proc_input.write(file_buffer.read_to_end());
                }
                None => {
                }
            }
        }
        None => {
        }
    }
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    let cmd = Cmd::new(pair[1].trim());
    match maybe(cmd, |c| make_process(c, None, None)) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }
    }
}

// Describes a computation that could fail.
// If v is Some(...), then f is called on v and the result is returned.
// Otherwise, None is returned.
fn maybe<A, B>(v : Option<A>, f : |A| -> Option<B>) -> Option<B> {
    match v {
        Some(v) => f(v),
        None    => None,
    }
}

// Same as maybe(v, f), but for &v.
fn borrowed_maybe<A, B>(v : &Option<A>, f : |&A| -> Option<B>) -> Option<B> {
    match *v {
        Some(ref v) => f(v),
        None    => None,
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => {
            let mut shell = Shell::new("");
            shell.run_cmdline(cmd_line);
        }
        None => {
            let mut shell = Shell::new("gash > ");
            shell.run();
        }
    }
}
<|endoftext|>


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

/* automatically generated by rust-bindgen */

use libc;
use jsapi::*;
use jsfriendapi::JSJitInfo;
use jsval::JSVal;

type c_bool = libc::c_int;

pub struct ProxyTraps {
    pub getPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, c_bool, *mut JSPropertyDescriptor) -> c_bool>,
    pub getOwnPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, JSBool, *mut JSPropertyDescriptor) -> JSBool>,
    pub defineProperty: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *JSPropertyDescriptor) -> JSBool>,
    pub getOwnPropertyNames: *u8, //XXX need a representation for AutoIdVector&
    pub delete_: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> JSBool>,
    pub enumerate: *u8, //XXX need a representation for AutoIdVector&

    pub has: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub hasOwn: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub get: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, *mut JSVal) -> JSBool>,
    pub set: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, JSBool, *mut JSVal) -> JSBool>,
    pub keys: *u8, //XXX need a representation for AutoIdVector&
    pub iterate: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,

    pub call: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,
    pub construct: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal, *mut JSVal) -> JSBool>,
    pub nativeCall: *u8, //XXX need a representation for IsAcceptableThis, NativeImpl, and CallArgs
    pub hasInstance: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal, *mut JSBool) -> JSBool>,
    pub typeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> uint>, //XXX JSType enum
    pub objectClassIs: Option<extern "C" fn(*mut JSObject, uint, *mut JSContext) -> JSBool>, //XXX ESClassValue enum
    pub obj_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> *mut JSString>,
    pub fun_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint) -> *mut JSString>,
    //regexp_toShared: *u8,
    pub defaultValue: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>, //XXX JSType enum
    pub iteratorNext: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal) -> JSBool>,
    pub finalize: Option<extern "C" fn(*mut JSFreeOp, *mut JSObject)>,
    pub getElementIfPresent: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, u32, *mut JSVal, *mut JSBool) -> JSBool>,
    pub getPrototypeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject, **mut JSObject) -> JSBool>,
    pub trace: Option<extern "C" fn(*mut JSTracer, *mut JSObject)>,
}

#[link(name = "jsglue")]
extern { }

#[cfg(target_os = "android")]
#[link_args = "-ljsglue -lstdc++ -lgcc -rdynamic"]
extern { }

extern {

//#[rust_stack]
pub fn RUST_JS_NumberValue(d: f64) -> JSVal;

//#[rust_stack]
pub fn CallJitPropertyOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn CallJitMethodOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, argc: libc::c_uint, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JSVal) -> *JSJitInfo;

pub fn SetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t, val: *mut JSVal);
pub fn GetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t) -> *mut JSVal;

pub fn CreateProxyHandler(traps: *ProxyTraps, extra: *libc::c_void) -> *libc::c_void;
pub fn CreateWrapperProxyHandler(traps: *ProxyTraps) -> *libc::c_void;
pub fn NewProxyObject(cx: *mut JSContext, handler: *libc::c_void, priv_: *mut JSVal,
                      proto: *mut JSObject, parent: *mut JSObject, call: *mut JSObject,
                      construct: *mut JSObject) -> *mut JSObject;
pub fn WrapperNew(cx: *mut JSContext, parent: *mut JSObject, handler: *libc::c_void) -> *mut JSObject;

pub fn GetProxyExtra(obj: *mut JSObject, slot: libc::c_uint) -> JSVal;
pub fn GetProxyPrivate(obj: *mut JSObject) -> JSVal;
pub fn SetProxyExtra(obj: *mut JSObject, slot: libc::c_uint, val: JSVal);

pub fn GetObjectProto(obj: *mut JSObject) -> *mut JSObject;
pub fn GetObjectParent(obj: *mut JSObject) -> *mut JSObject;

pub fn RUST_JSID_IS_INT(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_INT(id: jsid) -> libc::c_int;
pub fn RUST_JSID_IS_STRING(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_STRING(id: jsid) -> *mut JSString;

pub fn RUST_SET_JITINFO(func: *JSFunction, info: *JSJitInfo);

pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;

pub fn DefineFunctionWithReserved(cx: *mut JSContext, obj: *mut JSObject,
                                  name: *libc::c_char, call: JSNative, nargs: libc::c_uint,
                                  attrs: libc::c_uint) -> *mut JSObject;
pub fn GetObjectJSClass(obj: *mut JSObject) -> *JSClass;
pub fn RUST_js_GetErrorMessage(userRef: *libc::c_void, locale: *libc::c_char,
                               errorNumber: libc::c_uint) -> *JSErrorFormatString;
pub fn js_IsObjectProxyClass(obj: *mut JSObject) -> bool;
pub fn js_IsFunctionProxyClass(obj: *mut JSObject) -> bool;
pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> bool;
pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *libc::c_void;
pub fn GetProxyHandler(obj: *mut JSObject) -> *libc::c_void;
pub fn InvokeGetOwnPropertyDescriptor(handler: *libc::c_void, cx: *mut JSContext, proxy: *mut JSObject, id: jsid, set: JSBool, desc: *mut JSPropertyDescriptor) -> JSBool;
pub fn GetGlobalForObjectCrossCompartment(obj: *mut JSObject) -> *mut JSObject;
pub fn ReportError(cx: *mut JSContext, error: *libc::c_char);
pub fn IsWrapper(obj: *mut JSObject) -> JSBool;
pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: JSBool, flags: *libc::c_uint) -> *mut JSObject;
}
<commit_msg>Pass an immutable pointer to the private in NewProxyObject.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* automatically generated by rust-bindgen */

use libc;
use jsapi::*;
use jsfriendapi::JSJitInfo;
use jsval::JSVal;

type c_bool = libc::c_int;

pub struct ProxyTraps {
    pub getPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, c_bool, *mut JSPropertyDescriptor) -> c_bool>,
    pub getOwnPropertyDescriptor: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, JSBool, *mut JSPropertyDescriptor) -> JSBool>,
    pub defineProperty: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *JSPropertyDescriptor) -> JSBool>,
    pub getOwnPropertyNames: *u8, //XXX need a representation for AutoIdVector&
    pub delete_: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> JSBool>,
    pub enumerate: *u8, //XXX need a representation for AutoIdVector&

    pub has: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub hasOwn: Option<extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSBool) -> JSBool>,
    pub get: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, *mut JSVal) -> JSBool>,
    pub set: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, JSBool, *mut JSVal) -> JSBool>,
    pub keys: *u8, //XXX need a representation for AutoIdVector&
    pub iterate: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,

    pub call: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>,
    pub construct: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal, *mut JSVal) -> JSBool>,
    pub nativeCall: *u8, //XXX need a representation for IsAcceptableThis, NativeImpl, and CallArgs
    pub hasInstance: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal, *mut JSBool) -> JSBool>,
    pub typeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> uint>, //XXX JSType enum
    pub objectClassIs: Option<extern "C" fn(*mut JSObject, uint, *mut JSContext) -> JSBool>, //XXX ESClassValue enum
    pub obj_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject) -> *mut JSString>,
    pub fun_toString: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint) -> *mut JSString>,
    //regexp_toShared: *u8,
    pub defaultValue: Option<extern "C" fn(*mut JSContext, *mut JSObject, uint, *mut JSVal) -> JSBool>, //XXX JSType enum
    pub iteratorNext: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSVal) -> JSBool>,
    pub finalize: Option<extern "C" fn(*mut JSFreeOp, *mut JSObject)>,
    pub getElementIfPresent: Option<extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, u32, *mut JSVal, *mut JSBool) -> JSBool>,
    pub getPrototypeOf: Option<extern "C" fn(*mut JSContext, *mut JSObject, **mut JSObject) -> JSBool>,
    pub trace: Option<extern "C" fn(*mut JSTracer, *mut JSObject)>,
}

#[link(name = "jsglue")]
extern { }

#[cfg(target_os = "android")]
#[link_args = "-ljsglue -lstdc++ -lgcc -rdynamic"]
extern { }

extern {

//#[rust_stack]
pub fn RUST_JS_NumberValue(d: f64) -> JSVal;

//#[rust_stack]
pub fn CallJitPropertyOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn CallJitMethodOp(info: *JSJitInfo, cx: *mut JSContext, thisObj: *mut JSObject, specializedThis: *libc::c_void, argc: libc::c_uint, vp: *mut JSVal) -> JSBool;

//#[rust_stack]
pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JSVal) -> *JSJitInfo;

pub fn SetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t, val: *mut JSVal);
pub fn GetFunctionNativeReserved(fun: *mut JSObject, which: libc::size_t) -> *mut JSVal;

pub fn CreateProxyHandler(traps: *ProxyTraps, extra: *libc::c_void) -> *libc::c_void;
pub fn CreateWrapperProxyHandler(traps: *ProxyTraps) -> *libc::c_void;
pub fn NewProxyObject(cx: *mut JSContext, handler: *libc::c_void, priv_: *JSVal,
                      proto: *mut JSObject, parent: *mut JSObject, call: *mut JSObject,
                      construct: *mut JSObject) -> *mut JSObject;
pub fn WrapperNew(cx: *mut JSContext, parent: *mut JSObject, handler: *libc::c_void) -> *mut JSObject;

pub fn GetProxyExtra(obj: *mut JSObject, slot: libc::c_uint) -> JSVal;
pub fn GetProxyPrivate(obj: *mut JSObject) -> JSVal;
pub fn SetProxyExtra(obj: *mut JSObject, slot: libc::c_uint, val: JSVal);

pub fn GetObjectProto(obj: *mut JSObject) -> *mut JSObject;
pub fn GetObjectParent(obj: *mut JSObject) -> *mut JSObject;

pub fn RUST_JSID_IS_INT(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_INT(id: jsid) -> libc::c_int;
pub fn RUST_JSID_IS_STRING(id: jsid) -> JSBool;
pub fn RUST_JSID_TO_STRING(id: jsid) -> *mut JSString;

pub fn RUST_SET_JITINFO(func: *JSFunction, info: *JSJitInfo);

pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;

pub fn DefineFunctionWithReserved(cx: *mut JSContext, obj: *mut JSObject,
                                  name: *libc::c_char, call: JSNative, nargs: libc::c_uint,
                                  attrs: libc::c_uint) -> *mut JSObject;
pub fn GetObjectJSClass(obj: *mut JSObject) -> *JSClass;
pub fn RUST_js_GetErrorMessage(userRef: *libc::c_void, locale: *libc::c_char,
                               errorNumber: libc::c_uint) -> *JSErrorFormatString;
pub fn js_IsObjectProxyClass(obj: *mut JSObject) -> bool;
pub fn js_IsFunctionProxyClass(obj: *mut JSObject) -> bool;
pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> bool;
pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *libc::c_void;
pub fn GetProxyHandler(obj: *mut JSObject) -> *libc::c_void;
pub fn InvokeGetOwnPropertyDescriptor(handler: *libc::c_void, cx: *mut JSContext, proxy: *mut JSObject, id: jsid, set: JSBool, desc: *mut JSPropertyDescriptor) -> JSBool;
pub fn GetGlobalForObjectCrossCompartment(obj: *mut JSObject) -> *mut JSObject;
pub fn ReportError(cx: *mut JSContext, error: *libc::c_char);
pub fn IsWrapper(obj: *mut JSObject) -> JSBool;
pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: JSBool, flags: *libc::c_uint) -> *mut JSObject;
}
<|endoftext|>


<commit_before>mod idt;
<commit_msg>Create a static IDT with a page fault handler function<commit_after>mod idt;

lazy_static! {
    static ref IDT: idt::Idt = {
        let mut idt = idt::Idt::new();

        idt.set_handler(14, page_fault_handler);

        idt
    };
}

pub fn init() {
    IDT.load();
}

extern "C" fn page_fault_handler() -> ! {
    println!("EXCEPTION: PAGE FAULT");
    loop {}
}
<|endoftext|>