<commit_before>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    unmerged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            unmerged: false,
            ignored: false,
        };
        let err = "Bad input";
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    let key = words.next().ok_or(err)?;
                    let value = words.next().ok_or(err)?;
                    match key {
                        "branch.oid" => s.oid = String::from(value),
                        "branch.head" => s.branch = String::from(value),
                        "branch.upstream" => s.upstream = Some(String::from(value)),
                        "branch.ab" => {
                            if value != "+0" {
                                s.ahead = true;
                            }
                            if words.next().ok_or(err)? != "-0" {
                                s.behind = true;
                            }
                        },
                        _ => return Err(err),
                    }
                },
                Some("1") | Some("2") => {
                    let changes = words.next().ok_or(err)?;
                    let mut changes = changes.chars();
                    if changes.next().ok_or(err)? != '.' {
                        s.staged = true;
                    }
                    if changes.next().ok_or(err)? != '.' {
                        s.unstaged = true;
                    }
                },
                Some("u") => s.unmerged = true,
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err(err),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let mut symbols = String::new();
        if self.ahead {
            symbols.push('A');
        }
        if self.behind {
            symbols.push('B');
        }
        if self.staged {
            symbols.push('+');
        }
        if self.unstaged {
            symbols.push('*');
        }
        if self.untracked {
            symbols.push('?');
        }
        if self.ignored {
            symbols.push('!');
        }
        let short_oid = if self.oid == "(initial)" {
            &self.oid
        } else {
            &self.oid[..7]
        };
        if symbols.is_empty() {
            format!("[{} {}]", self.branch, short_oid)
        } else {
            format!("[{} {} {}]", self.branch, short_oid, symbols)
        }
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                unmerged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                unmerged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unmerged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
u UU N... 100644 100644 100644 100644 8fb20c5f0b7da31f56f74f0a98e1fadb13e4c2a0 801dd97d4dace6780f9eca5a99dbee77d6e05a95 cbf6eb8db76897842f3b77d1d2b95dbd422c180d README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.unmerged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                unmerged: false,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            unmerged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a]"));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            unmerged: false,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*?!]"));
    }

    #[test]
    fn test_to_line_initial_commit() {
        let s = GitStatus {
            oid: String::from("(initial)"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            unmerged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master (initial)]"));
    }
}
<commit_msg>Add unmerged status to symbols<commit_after>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    unmerged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            unmerged: false,
            ignored: false,
        };
        let err = "Bad input";
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    let key = words.next().ok_or(err)?;
                    let value = words.next().ok_or(err)?;
                    match key {
                        "branch.oid" => s.oid = String::from(value),
                        "branch.head" => s.branch = String::from(value),
                        "branch.upstream" => s.upstream = Some(String::from(value)),
                        "branch.ab" => {
                            if value != "+0" {
                                s.ahead = true;
                            }
                            if words.next().ok_or(err)? != "-0" {
                                s.behind = true;
                            }
                        },
                        _ => return Err(err),
                    }
                },
                Some("1") | Some("2") => {
                    let changes = words.next().ok_or(err)?;
                    let mut changes = changes.chars();
                    if changes.next().ok_or(err)? != '.' {
                        s.staged = true;
                    }
                    if changes.next().ok_or(err)? != '.' {
                        s.unstaged = true;
                    }
                },
                Some("u") => s.unmerged = true,
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err(err),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let mut symbols = String::new();
        if self.ahead {
            symbols.push('A');
        }
        if self.behind {
            symbols.push('B');
        }
        if self.staged {
            symbols.push('+');
        }
        if self.unstaged {
            symbols.push('*');
        }
        if self.unmerged {
            symbols.push('%');
        }
        if self.untracked {
            symbols.push('?');
        }
        if self.ignored {
            symbols.push('!');
        }
        let short_oid = if self.oid == "(initial)" {
            &self.oid
        } else {
            &self.oid[..7]
        };
        if symbols.is_empty() {
            format!("[{} {}]", self.branch, short_oid)
        } else {
            format!("[{} {} {}]", self.branch, short_oid, symbols)
        }
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                unmerged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                unmerged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unmerged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
u UU N... 100644 100644 100644 100644 8fb20c5f0b7da31f56f74f0a98e1fadb13e4c2a0 801dd97d4dace6780f9eca5a99dbee77d6e05a95 cbf6eb8db76897842f3b77d1d2b95dbd422c180d README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.unmerged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                unmerged: false,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            unmerged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a]"));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            unmerged: true,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*%?!]"));
    }

    #[test]
    fn test_to_line_initial_commit() {
        let s = GitStatus {
            oid: String::from("(initial)"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            unmerged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master (initial)]"));
    }
}
<|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/. */

#![crate_name = "js"]
#![crate_type = "rlib"]

#![feature(globs, link_args, phase, unsafe_destructor)]

#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case, improper_ctypes)]

extern crate libc;
#[phase(plugin, link)]
extern crate log;
extern crate serialize;

use libc::c_uint;
use libc::types::common::c99::uint32_t;
use jsapi::{JSBool, JSContext, JSPropertyOp, JSStrictPropertyOp, JSEnumerateOp,
            JSObject, jsid, JSResolveOp, JSConvertOp, JSFinalizeOp, JSTraceOp,
            JSProto_LIMIT, JSHandleObject, JSCheckAccessOp, JSNative, JSHasInstanceOp};
use jsapi::JS_ComputeThis;
use jsval::JSVal;

// These are just macros in jsapi.h
pub use jsapi::JS_Init as JS_NewRuntime;
pub use jsapi::JS_Finish as JS_DestroyRuntime;
/*
FIXME: Not sure where JS_Lock is
pub use jsapi::bindgen::JS_Lock as JS_LockRuntime;
pub use jsapi::bindgen::JS_Unlock as JS_UnlockRuntime;
*/

pub use jsfriendapi::JSJitInfo;

pub mod jsapi;
pub mod linkhack;
pub mod rust;
pub mod glue;
pub mod trace;
pub mod jsval;
pub mod jsfriendapi;

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
pub const JSOPTION_METHODJIT: uint32_t = (1u32 << 14) as u32;
pub const JSOPTION_TYPE_INFERENCE: uint32_t = (1u32 << 18) as u32;

pub const default_heapsize: u32 = 32_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSID_TYPE_STRING: i64 = 0;
pub const JSID_TYPE_INT: i64 = 1;
pub const JSID_TYPE_VOID: i64 = 2;
pub const JSID_TYPE_OBJECT: i64 = 4;
pub const JSID_TYPE_DEFAULT_XML_NAMESPACE: i64 = 6;
pub const JSID_TYPE_MASK: i64 = 7;

pub const JSID_VOID: jsid = JSID_TYPE_VOID as jsid;

pub const JSFUN_CONSTRUCTOR: u32 = 0x200; /* native that can be called as a ctor */

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_PERMANENT: c_uint = 0x04;
pub const JSPROP_GETTER: c_uint = 0x10;
pub const JSPROP_SETTER: c_uint = 0x20;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1u << JSCLASS_RESERVED_SLOTS_WIDTH as uint) - 1) as c_uint;

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<((JSCLASS_HIGH_FLAGS_SHIFT as uint)+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub const JSCLASS_IS_DOMJSCLASS: u32 = 1 << 4;
pub const JSCLASS_USERBIT1: u32 = 1 << 7;

pub const JSSLOT_PROXY_PRIVATE: u32 = 1;

pub const JSRESOLVE_QUALIFIED: u32 = 0x01;
pub const JSRESOLVE_ASSIGNING: u32 = 0x02;
pub const JSRESOLVE_DETECTING: u32 = 0x04;

pub enum JSGCTraceKind {
    JSTRACE_OBJECT,
    JSTRACE_STRING,
    JSTRACE_SCRIPT
}

pub fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << (JSCLASS_RESERVED_SLOTS_SHIFT as uint)
}

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JSVal) -> *mut JSVal {
    vp.offset(2)
}

pub unsafe fn JS_SET_RVAL(_cx: *mut JSContext, vp: *mut JSVal, v: JSVal) {
    *vp = v;
}

#[inline(alwyas)]
pub unsafe fn JS_THIS_OBJECT(cx: *mut JSContext, vp: *mut JSVal) -> *mut JSObject {
    let r =
        if (*(vp.offset(1))).is_primitive() {
            JS_ComputeThis(cx, vp)
        } else {
            *(vp.offset(1))
        };
    r.to_object_or_null()
}

#[inline(always)]
pub unsafe fn JS_CALLEE(_cx: *mut JSContext, vp: *mut JSVal) -> JSVal {
    *vp
}

pub type JSObjectOp = extern "C" fn(*mut JSContext, JSHandleObject) -> *mut JSObject;

#[deriving(Copy)]
pub struct Class {
    pub name: *const libc::c_char,
    pub flags: uint32_t,
    pub addProperty: JSPropertyOp,
    pub delProperty: JSPropertyOp,
    pub getProperty: JSPropertyOp,
    pub setProperty: JSStrictPropertyOp,
    pub enumerate: JSEnumerateOp,
    pub resolve: JSResolveOp,
    pub convert: JSConvertOp,
    pub finalize: JSFinalizeOp,
    pub checkAccess: JSCheckAccessOp,
    pub call: JSNative,
    pub hasInstance: JSHasInstanceOp,
    pub construct: JSNative,
    pub trace: JSTraceOp,

    pub ext: ClassExtension,
    pub ops: ObjectOps,
}

pub struct ClassExtension {
    pub equality: *const u8,
    pub outerObject: Option<JSObjectOp>,
    pub innerObject: Option<JSObjectOp>,
    pub iteratorObject: *const u8,
    pub unused: *const u8,
    pub isWrappedNative: *const u8,
}

pub struct ObjectOps {
    pub lookupGeneric: *const u8,
    pub lookupProperty: *const u8,
    pub lookupElement: *const u8,
    pub lookupSpecial: *const u8,
    pub defineGeneric: *const u8,
    pub defineProperty: *const u8,
    pub defineElement: *const u8,
    pub defineSpecial: *const u8,
    pub getGeneric: *const u8,
    pub getProperty: *const u8,
    pub getElement: *const u8,
    pub getElementIfPresent: *const u8,
    pub getSpecial: *const u8,
    pub setGeneric: *const u8,
    pub setProperty: *const u8,
    pub setElement: *const u8,
    pub setSpecial: *const u8,
    pub getGenericAttributes: *const u8,
    pub getPropertyAttributes: *const u8,
    pub getElementAttributes: *const u8,
    pub getSpecialAttributes: *const u8,
    pub setGenericAttributes: *const u8,
    pub setPropertyAttributes: *const u8,
    pub setElementAttributes: *const u8,
    pub setSpecialAttributes: *const u8,
    pub deleteProperty: *const u8,
    pub deleteElement: *const u8,
    pub deleteSpecial: *const u8,

    pub enumerate: *const u8,
    pub typeOf: *const u8,
    pub thisObject: Option<JSObjectOp>,
    pub clear: *const u8,
}
<commit_msg>fixup! Derive Copy for js::Class.<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/. */

#![crate_name = "js"]
#![crate_type = "rlib"]

#![feature(globs, link_args, phase, unsafe_destructor)]

#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case, improper_ctypes)]

extern crate libc;
#[phase(plugin, link)]
extern crate log;
extern crate serialize;

use libc::c_uint;
use libc::types::common::c99::uint32_t;
use jsapi::{JSBool, JSContext, JSPropertyOp, JSStrictPropertyOp, JSEnumerateOp,
            JSObject, jsid, JSResolveOp, JSConvertOp, JSFinalizeOp, JSTraceOp,
            JSProto_LIMIT, JSHandleObject, JSCheckAccessOp, JSNative, JSHasInstanceOp};
use jsapi::JS_ComputeThis;
use jsval::JSVal;

// These are just macros in jsapi.h
pub use jsapi::JS_Init as JS_NewRuntime;
pub use jsapi::JS_Finish as JS_DestroyRuntime;
/*
FIXME: Not sure where JS_Lock is
pub use jsapi::bindgen::JS_Lock as JS_LockRuntime;
pub use jsapi::bindgen::JS_Unlock as JS_UnlockRuntime;
*/

pub use jsfriendapi::JSJitInfo;

pub mod jsapi;
pub mod linkhack;
pub mod rust;
pub mod glue;
pub mod trace;
pub mod jsval;
pub mod jsfriendapi;

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
pub const JSOPTION_METHODJIT: uint32_t = (1u32 << 14) as u32;
pub const JSOPTION_TYPE_INFERENCE: uint32_t = (1u32 << 18) as u32;

pub const default_heapsize: u32 = 32_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSID_TYPE_STRING: i64 = 0;
pub const JSID_TYPE_INT: i64 = 1;
pub const JSID_TYPE_VOID: i64 = 2;
pub const JSID_TYPE_OBJECT: i64 = 4;
pub const JSID_TYPE_DEFAULT_XML_NAMESPACE: i64 = 6;
pub const JSID_TYPE_MASK: i64 = 7;

pub const JSID_VOID: jsid = JSID_TYPE_VOID as jsid;

pub const JSFUN_CONSTRUCTOR: u32 = 0x200; /* native that can be called as a ctor */

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_PERMANENT: c_uint = 0x04;
pub const JSPROP_GETTER: c_uint = 0x10;
pub const JSPROP_SETTER: c_uint = 0x20;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1u << JSCLASS_RESERVED_SLOTS_WIDTH as uint) - 1) as c_uint;

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<((JSCLASS_HIGH_FLAGS_SHIFT as uint)+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub const JSCLASS_IS_DOMJSCLASS: u32 = 1 << 4;
pub const JSCLASS_USERBIT1: u32 = 1 << 7;

pub const JSSLOT_PROXY_PRIVATE: u32 = 1;

pub const JSRESOLVE_QUALIFIED: u32 = 0x01;
pub const JSRESOLVE_ASSIGNING: u32 = 0x02;
pub const JSRESOLVE_DETECTING: u32 = 0x04;

pub enum JSGCTraceKind {
    JSTRACE_OBJECT,
    JSTRACE_STRING,
    JSTRACE_SCRIPT
}

pub fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << (JSCLASS_RESERVED_SLOTS_SHIFT as uint)
}

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JSVal) -> *mut JSVal {
    vp.offset(2)
}

pub unsafe fn JS_SET_RVAL(_cx: *mut JSContext, vp: *mut JSVal, v: JSVal) {
    *vp = v;
}

#[inline(alwyas)]
pub unsafe fn JS_THIS_OBJECT(cx: *mut JSContext, vp: *mut JSVal) -> *mut JSObject {
    let r =
        if (*(vp.offset(1))).is_primitive() {
            JS_ComputeThis(cx, vp)
        } else {
            *(vp.offset(1))
        };
    r.to_object_or_null()
}

#[inline(always)]
pub unsafe fn JS_CALLEE(_cx: *mut JSContext, vp: *mut JSVal) -> JSVal {
    *vp
}

pub type JSObjectOp = extern "C" fn(*mut JSContext, JSHandleObject) -> *mut JSObject;

#[deriving(Copy)]
pub struct Class {
    pub name: *const libc::c_char,
    pub flags: uint32_t,
    pub addProperty: JSPropertyOp,
    pub delProperty: JSPropertyOp,
    pub getProperty: JSPropertyOp,
    pub setProperty: JSStrictPropertyOp,
    pub enumerate: JSEnumerateOp,
    pub resolve: JSResolveOp,
    pub convert: JSConvertOp,
    pub finalize: JSFinalizeOp,
    pub checkAccess: JSCheckAccessOp,
    pub call: JSNative,
    pub hasInstance: JSHasInstanceOp,
    pub construct: JSNative,
    pub trace: JSTraceOp,

    pub ext: ClassExtension,
    pub ops: ObjectOps,
}

#[deriving(Copy)]
pub struct ClassExtension {
    pub equality: *const u8,
    pub outerObject: Option<JSObjectOp>,
    pub innerObject: Option<JSObjectOp>,
    pub iteratorObject: *const u8,
    pub unused: *const u8,
    pub isWrappedNative: *const u8,
}

#[deriving(Copy)]
pub struct ObjectOps {
    pub lookupGeneric: *const u8,
    pub lookupProperty: *const u8,
    pub lookupElement: *const u8,
    pub lookupSpecial: *const u8,
    pub defineGeneric: *const u8,
    pub defineProperty: *const u8,
    pub defineElement: *const u8,
    pub defineSpecial: *const u8,
    pub getGeneric: *const u8,
    pub getProperty: *const u8,
    pub getElement: *const u8,
    pub getElementIfPresent: *const u8,
    pub getSpecial: *const u8,
    pub setGeneric: *const u8,
    pub setProperty: *const u8,
    pub setElement: *const u8,
    pub setSpecial: *const u8,
    pub getGenericAttributes: *const u8,
    pub getPropertyAttributes: *const u8,
    pub getElementAttributes: *const u8,
    pub getSpecialAttributes: *const u8,
    pub setGenericAttributes: *const u8,
    pub setPropertyAttributes: *const u8,
    pub setElementAttributes: *const u8,
    pub setSpecialAttributes: *const u8,
    pub deleteProperty: *const u8,
    pub deleteElement: *const u8,
    pub deleteSpecial: *const u8,

    pub enumerate: *const u8,
    pub typeOf: *const u8,
    pub thisObject: Option<JSObjectOp>,
    pub clear: *const u8,
}
<|endoftext|>


<commit_before>#![allow(trivial_casts)]
#![feature(fs_time)]
#![cfg_attr(test, deny(warnings))]

extern crate conduit;
extern crate conduit_mime_types as mime;
extern crate time;
#[cfg(test)] extern crate tempdir;

use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::io::prelude::*;
use std::path::{PathBuf, Path};
use std::fs::File;
use conduit::{Request, Response, Handler};

pub struct Static {
    path: PathBuf,
    types: mime::Types
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new()
                .ok().expect("Couldn't load mime-types")
        }
    }
}

impl Handler for Static {
    fn call(&self, request: &mut Request) -> Result<Response, Box<Error+Send>> {
        let request_path = &request.path()[1..];
        if request_path.contains("..") { return Ok(not_found()) }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = try!(file.metadata().map_err(|e| Box::new(e) as Box<Error+Send>));
        if data.is_dir() {
            return Ok(not_found())
        }
        let ts = time::Timespec {
            sec: (data.modified() as i64) / 1000,
            nsec: (((data.modified() as u32) % 1000) as i32) * 1000000
        };
        let tm = time::at(ts).to_utc();

        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), vec![mime.to_string()]);
        headers.insert("Content-Length".to_string(),
                       vec![data.len().to_string()]);
        headers.insert("Last-Modified".to_string(),
                       vec![tm.strftime("%a, %d %b %Y %T GMT").unwrap()
                              .to_string()]);

        Ok(Response {
            status: (200, "OK"),
            headers: headers,
            body: Box::new(file),
        })
    }
}

fn not_found() -> Response {
    Response {
        status: (404, "Not Found"),
        headers: HashMap::new(),
        body: Box::new(io::empty()),
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use conduit::{Handler, Method};
    use Static;

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root.clone());
        File::create(&root.join("Cargo.toml")).unwrap()
             .write_all(b"[package]").unwrap();
        let mut req = test::MockRequest::new(Method::Get, "/Cargo.toml");
        let mut res = handler.call(&mut req).ok().expect("No response");
        let mut body = String::new();
        res.body.read_to_string(&mut body).unwrap();
        assert_eq!(body, "[package]");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/plain".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["9".to_string()]));
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/src/fixture.css");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/css".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["0".to_string()]));
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/nope");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/foo");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/test");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 200);
        assert!(res.headers.get("Last-Modified").is_some());
    }
}
<commit_msg>Allow deprecation warnings for now<commit_after>#![allow(trivial_casts)]
#![feature(fs_time)]
#![cfg_attr(test, deny(warnings))]

extern crate conduit;
extern crate conduit_mime_types as mime;
extern crate time;
#[cfg(test)] extern crate tempdir;

use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::io::prelude::*;
use std::path::{PathBuf, Path};
use std::fs::File;
use conduit::{Request, Response, Handler};

pub struct Static {
    path: PathBuf,
    types: mime::Types
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new()
                .ok().expect("Couldn't load mime-types")
        }
    }
}

impl Handler for Static {
    #[allow(deprecated)]
    fn call(&self, request: &mut Request) -> Result<Response, Box<Error+Send>> {
        let request_path = &request.path()[1..];
        if request_path.contains("..") { return Ok(not_found()) }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = try!(file.metadata().map_err(|e| Box::new(e) as Box<Error+Send>));
        if data.is_dir() {
            return Ok(not_found())
        }
        let ts = time::Timespec {
            sec: (data.modified() as i64) / 1000,
            nsec: (((data.modified() as u32) % 1000) as i32) * 1000000
        };
        let tm = time::at(ts).to_utc();

        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), vec![mime.to_string()]);
        headers.insert("Content-Length".to_string(),
                       vec![data.len().to_string()]);
        headers.insert("Last-Modified".to_string(),
                       vec![tm.strftime("%a, %d %b %Y %T GMT").unwrap()
                              .to_string()]);

        Ok(Response {
            status: (200, "OK"),
            headers: headers,
            body: Box::new(file),
        })
    }
}

fn not_found() -> Response {
    Response {
        status: (404, "Not Found"),
        headers: HashMap::new(),
        body: Box::new(io::empty()),
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use conduit::{Handler, Method};
    use Static;

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root.clone());
        File::create(&root.join("Cargo.toml")).unwrap()
             .write_all(b"[package]").unwrap();
        let mut req = test::MockRequest::new(Method::Get, "/Cargo.toml");
        let mut res = handler.call(&mut req).ok().expect("No response");
        let mut body = String::new();
        res.body.read_to_string(&mut body).unwrap();
        assert_eq!(body, "[package]");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/plain".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["9".to_string()]));
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/src/fixture.css");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/css".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["0".to_string()]));
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/nope");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/foo");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/test");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 200);
        assert!(res.headers.get("Last-Modified").is_some());
    }
}
<|endoftext|>


<commit_before>//! An XML-RPC implementation in Rust.
//!
//! The `xmlrpc` crate provides a minimal implementation of the [XML-RPC spec][spec].
//!
//! [Repo link][repo].
//!
//! [spec]: http://xmlrpc.scripting.com/spec.html
//! [repo]: https://github.com/jonas-schievink/xml-rpc-rs

extern crate base64;
extern crate iso8601;
extern crate xml;

mod error;
mod parser;
mod request;
mod value;
mod utils;
mod transport;

pub use error::{RequestError, Fault};
pub use request::{Request, RequestResult};
pub use value::Value;
pub use transport::Transport;

#[cfg(feature = "reqwest")]
pub use transport::http;

/// A response from the server.
///
/// XML-RPC specifies that a call should either return a single `Value`, or a `<fault>`.
pub type Response = Result<Value, Fault>;
<commit_msg>Remove repo link from crate docs<commit_after>//! An XML-RPC implementation in Rust.
//!
//! The `xmlrpc` crate provides a minimal implementation of the [XML-RPC spec][spec].
//!
//!
//! [spec]: http://xmlrpc.scripting.com/spec.html

extern crate base64;
extern crate iso8601;
extern crate xml;

mod error;
mod parser;
mod request;
mod value;
mod utils;
mod transport;

pub use error::{RequestError, Fault};
pub use request::{Request, RequestResult};
pub use value::Value;
pub use transport::Transport;

#[cfg(feature = "reqwest")]
pub use transport::http;

/// A response from the server.
///
/// XML-RPC specifies that a call should either return a single `Value`, or a `<fault>`.
pub type Response = Result<Value, Fault>;
<|endoftext|>


<commit_before>extern crate num;

// Must re-export num for the unit! macro.
#[doc(hidden)]pub use num as num_export;

mod vec2;
mod vec3;
mod vec4;
mod float;
mod clamp;
#[macro_use] pub mod unit;

pub use num::{Zero, One, Num};
pub use self::vec2::{Vec2, Point2};
pub use self::vec3::{Vec3, Point3};
pub use self::vec4::Vec4;
pub use self::float::{EPSILON, Float};
pub use self::clamp::Clamp;
<commit_msg>Fix formatting on reexport of num<commit_after>extern crate num;

// Must re-export num for the unit! macro.
#[doc(hidden)] pub use num as num_export;

mod vec2;
mod vec3;
mod vec4;
mod float;
mod clamp;
#[macro_use] pub mod unit;

pub use num::{Zero, One, Num};
pub use self::vec2::{Vec2, Point2};
pub use self::vec3::{Vec3, Point3};
pub use self::vec4::Vec4;
pub use self::float::{EPSILON, Float};
pub use self::clamp::Clamp;
<|endoftext|>


<commit_before>use core::fmt;

/// x86 Exception description (see also Intel Vol. 3a Chapter 6).
#[derive(Debug)]
pub struct InterruptDescription {
    pub vector: u8,
    pub mnemonic: &'static str,
    pub description: &'static str,
    pub irqtype: &'static str,
    pub source: &'static str
}

impl fmt::Display for InterruptDescription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({}, vec={}) {}", self.mnemonic, self.irqtype, self.vector, self.description)
    }
}

/// x86 External Interrupts (1-16).
pub static EXCEPTIONS: [InterruptDescription; 15] = [
    InterruptDescription{ vector: 0,  mnemonic: "#DE", description: "Divide Error", irqtype: "Fault", source: "DIV and IDIV instructions." },
    InterruptDescription{ vector: 1,  mnemonic: "#DB", description: "RESERVED", irqtype: "Fault/ Trap", source: "For Intel use only." },
    InterruptDescription{ vector: 2,  mnemonic: "NMI", description: "Interrupt", irqtype: "Interrupt", source: "Nonmaskable external interrupt." },
    InterruptDescription{ vector: 3,  mnemonic: "#BP", description: "Breakpoint", irqtype: "Trap", source: "INT 3 instruction." },
    InterruptDescription{ vector: 4,  mnemonic: "#OF", description: "Overflow", irqtype: "Trap", source: "INTO instruction." },
    InterruptDescription{ vector: 5,  mnemonic: "#BR", description: "BOUND Range Exceeded", irqtype: "Fault", source: "BOUND instruction." },
    InterruptDescription{ vector: 6,  mnemonic: "#UD", description: "Invalid Opcode (Undefined Opcode)", irqtype: "Fault", source: "UD2 instruction or reserved opcode." },
    InterruptDescription{ vector: 7,  mnemonic: "#NM", description: "Device Not Available (No Math Coprocessor)", irqtype: "Fault", source: "Floating-point or WAIT/FWAIT instruction." },
    InterruptDescription{ vector: 8,  mnemonic: "#DF", description: "Double Fault", irqtype: "Abort", source: "Any instruction that can generate an exception, an NMI, or an INTR." },
    InterruptDescription{ vector: 9,  mnemonic: ""   , description: "Coprocessor Segment Overrun", irqtype: "Fault", source: "Floating-point instruction." },
    InterruptDescription{ vector: 10, mnemonic: "#TS", description: "Invalid TSS", irqtype: "Fault", source: "Task switch or TSS access." },
    InterruptDescription{ vector: 11, mnemonic: "#NP", description: "Segment Not Present", irqtype: "Fault", source: "Loading segment registers or accessing system segments." },
    InterruptDescription{ vector: 12, mnemonic: "#SS", description: "Stack-Segment Fault", irqtype: "Fault", source: "Stack operations and SS register loads." },
    InterruptDescription{ vector: 13, mnemonic: "#GP", description: "General Protection", irqtype: "Fault", source: "Any memory reference and other protection checks." },
    InterruptDescription{ vector: 14, mnemonic: "#PF", description: "Page Fault", irqtype: "Fault", source: "Any memory reference." }
];

/// Enable Interrupts.
pub unsafe fn enable()  {
    asm!("sti");
}

/// Disable Interrupts.
pub unsafe fn disable()  {
    asm!("cli");
}

/// Generate a software interrupt.
/// This is a macro argument needs to be an immediate.
#[macro_export]
macro_rules! int {
    ( $x:expr ) => {
        {
            asm!("int $0" :: "N" ($x));
        }
    };
}

/// A struct describing an interrupt gate.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct IdtEntry {
    /// Lower 16 bits of ISR.
    pub base_lo: u16,
    /// Segment selector.
    pub sel: u16,
    /// This must always be zero.
    pub res0: u8,
    /// Flags.
    pub flags: u8,
    /// The upper 48 bits of ISR (the last 16 bits must be zero).
    pub base_hi: u64,
    /// Must be zero.
    pub res1: u16
}

bitflags!{
    // Taken from Intel Manual Section 4.7 Page-Fault Exceptions.
    flags PageFaultError: u32 {
        /// 0: The fault was caused by a non-present page.
        /// 1: The fault was caused by a page-level protection violation
        const PFAULT_ERROR_P = 0b000001,

        /// 0: The access causing the fault was a read.
        /// 1: The access causing the fault was a write.
        const PFAULT_ERROR_WR = 0b000010,

        /// 0: The access causing the fault originated when the processor
        /// was executing in supervisor mode.
        /// 1: The access causing the fault originated when the processor
        /// was executing in user mode.
        const PFAULT_ERROR_US = 0b000100,

        /// 0: The fault was not caused by reserved bit violation.
        /// 1: The fault was caused by reserved bits set to 1 in a page directory.
        const PFAULT_ERROR_RSVD = 0b001000,

        /// 0: The fault was not caused by an instruction fetch.
        /// 1: The fault was caused by an instruction fetch.
        const PFAULT_ERROR_ID = 0b100000,

        /// 0: The fault was not by protection keys.
        /// 1: There was a protection key violation.
        const PFAULT_ERROR_PK = 0b100000,
    }
}

impl fmt::Debug for PageFaultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = match self.contains(PFAULT_ERROR_P) {
            false => "The fault was caused by a non-present page.",
            true => "The fault was caused by a page-level protection violation."
        };
        let wr = match self.contains(PFAULT_ERROR_WR) {
            false => "The access causing the fault was a read.",
            true => "The access causing the fault was a write."
        };
        let us = match self.contains(PFAULT_ERROR_US) {
            false => "The access causing the fault originated when the processor was executing in supervisor mode.",
            true => "The access causing the fault originated when the processor was executing in user mode."
        };
        let rsvd = match self.contains(PFAULT_ERROR_RSVD) {
            false => "The fault was not caused by reserved bit violation.",
            true => "The fault was caused by reserved bits set to 1 in a page directory."
        };
        let id = match self.contains(PFAULT_ERROR_ID) {
            false => "The fault was not caused by an instruction fetch.",
            true => "The fault was caused by an instruction fetch."
        };

        write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id)
    }
}

<commit_msg>Fixes #3 -- correctly this time.<commit_after>use core::fmt;

/// x86 Exception description (see also Intel Vol. 3a Chapter 6).
#[derive(Debug)]
pub struct InterruptDescription {
    pub vector: u8,
    pub mnemonic: &'static str,
    pub description: &'static str,
    pub irqtype: &'static str,
    pub source: &'static str
}

impl fmt::Display for InterruptDescription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({}, vec={}) {}", self.mnemonic, self.irqtype, self.vector, self.description)
    }
}

/// x86 External Interrupts (1-16).
pub static EXCEPTIONS: [InterruptDescription; 15] = [
    InterruptDescription{ vector: 0,  mnemonic: "#DE", description: "Divide Error", irqtype: "Fault", source: "DIV and IDIV instructions." },
    InterruptDescription{ vector: 1,  mnemonic: "#DB", description: "RESERVED", irqtype: "Fault/ Trap", source: "For Intel use only." },
    InterruptDescription{ vector: 2,  mnemonic: "NMI", description: "Interrupt", irqtype: "Interrupt", source: "Nonmaskable external interrupt." },
    InterruptDescription{ vector: 3,  mnemonic: "#BP", description: "Breakpoint", irqtype: "Trap", source: "INT 3 instruction." },
    InterruptDescription{ vector: 4,  mnemonic: "#OF", description: "Overflow", irqtype: "Trap", source: "INTO instruction." },
    InterruptDescription{ vector: 5,  mnemonic: "#BR", description: "BOUND Range Exceeded", irqtype: "Fault", source: "BOUND instruction." },
    InterruptDescription{ vector: 6,  mnemonic: "#UD", description: "Invalid Opcode (Undefined Opcode)", irqtype: "Fault", source: "UD2 instruction or reserved opcode." },
    InterruptDescription{ vector: 7,  mnemonic: "#NM", description: "Device Not Available (No Math Coprocessor)", irqtype: "Fault", source: "Floating-point or WAIT/FWAIT instruction." },
    InterruptDescription{ vector: 8,  mnemonic: "#DF", description: "Double Fault", irqtype: "Abort", source: "Any instruction that can generate an exception, an NMI, or an INTR." },
    InterruptDescription{ vector: 9,  mnemonic: ""   , description: "Coprocessor Segment Overrun", irqtype: "Fault", source: "Floating-point instruction." },
    InterruptDescription{ vector: 10, mnemonic: "#TS", description: "Invalid TSS", irqtype: "Fault", source: "Task switch or TSS access." },
    InterruptDescription{ vector: 11, mnemonic: "#NP", description: "Segment Not Present", irqtype: "Fault", source: "Loading segment registers or accessing system segments." },
    InterruptDescription{ vector: 12, mnemonic: "#SS", description: "Stack-Segment Fault", irqtype: "Fault", source: "Stack operations and SS register loads." },
    InterruptDescription{ vector: 13, mnemonic: "#GP", description: "General Protection", irqtype: "Fault", source: "Any memory reference and other protection checks." },
    InterruptDescription{ vector: 14, mnemonic: "#PF", description: "Page Fault", irqtype: "Fault", source: "Any memory reference." }
];

/// Enable Interrupts.
pub unsafe fn enable()  {
    asm!("sti");
}

/// Disable Interrupts.
pub unsafe fn disable()  {
    asm!("cli");
}

/// Generate a software interrupt.
/// This is a macro argument needs to be an immediate.
#[macro_export]
macro_rules! int {
    ( $x:expr ) => {
        {
            asm!("int $0" :: "N" ($x));
        }
    };
}

/// A struct describing an interrupt gate.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct IdtEntry {
    /// Lower 16 bits of ISR.
    pub base_lo: u16,
    /// Segment selector.
    pub sel: u16,
    /// This must always be zero.
    pub res0: u8,
    /// Flags.
    pub flags: u8,
    /// The upper 48 bits of ISR (the last 16 bits must be zero).
    pub base_hi: u64,
    /// Must be zero.
    pub res1: u16
}

bitflags!{
    // Taken from Intel Manual Section 4.7 Page-Fault Exceptions.
    flags PageFaultError: u32 {
        /// 0: The fault was caused by a non-present page.
        /// 1: The fault was caused by a page-level protection violation
        const PFAULT_ERROR_P = 0b000001,

        /// 0: The access causing the fault was a read.
        /// 1: The access causing the fault was a write.
        const PFAULT_ERROR_WR = 0b000010,

        /// 0: The access causing the fault originated when the processor
        /// was executing in supervisor mode.
        /// 1: The access causing the fault originated when the processor
        /// was executing in user mode.
        const PFAULT_ERROR_US = 0b000100,

        /// 0: The fault was not caused by reserved bit violation.
        /// 1: The fault was caused by reserved bits set to 1 in a page directory.
        const PFAULT_ERROR_RSVD = 0b001000,

        /// 0: The fault was not caused by an instruction fetch.
        /// 1: The fault was caused by an instruction fetch.
        const PFAULT_ERROR_ID = 0b010000,

        /// 0: The fault was not by protection keys.
        /// 1: There was a protection key violation.
        const PFAULT_ERROR_PK = 0b100000,
    }
}

impl fmt::Debug for PageFaultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = match self.contains(PFAULT_ERROR_P) {
            false => "The fault was caused by a non-present page.",
            true => "The fault was caused by a page-level protection violation."
        };
        let wr = match self.contains(PFAULT_ERROR_WR) {
            false => "The access causing the fault was a read.",
            true => "The access causing the fault was a write."
        };
        let us = match self.contains(PFAULT_ERROR_US) {
            false => "The access causing the fault originated when the processor was executing in supervisor mode.",
            true => "The access causing the fault originated when the processor was executing in user mode."
        };
        let rsvd = match self.contains(PFAULT_ERROR_RSVD) {
            false => "The fault was not caused by reserved bit violation.",
            true => "The fault was caused by reserved bits set to 1 in a page directory."
        };
        let id = match self.contains(PFAULT_ERROR_ID) {
            false => "The fault was not caused by an instruction fetch.",
            true => "The fault was caused by an instruction fetch."
        };

        write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id)
    }
}

<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Nathan Sizemore <nathanrsizemore@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.

//! WebSocketStream crate

#![feature(libc, net, std_misc, os, collections, core)]

extern crate libc;

use std::mem;
use std::net::TcpStream;
use std::os;
use std::os::unix::prelude::AsRawFd;

use self::libc::consts::os::posix88;
use self::libc::{size_t, c_void, c_int, ssize_t};

use util::*;
pub mod util;

extern "system" {
    fn read(fd: c_int, buffer: *mut c_void, count: size_t) -> ssize_t;
    fn write(fd: c_int, buffer: *const c_void, cout: size_t) -> ssize_t;
}

pub type NewResult = Result<WebSocketStream, SetFdError>;
pub type ReadResult = Result<(OpCode, Vec<u8>), ReadError>;
pub type WriteResult = Result<u64, WriteError>;

type SysReadResult = Result<Vec<u8>, ReadError>;
type SysWriteResult = Result<u64, WriteError>;
type PayloadKeyResult = Result<u8, ReadError>;
type PayloadLenResult = Result<u64, ReadError>;

/// Represents a non-blocking RFC-6455 Protocol stream
pub struct WebSocketStream {
    stream: TcpStream
}

impl WebSocketStream {

    /// Creates a new WebSocket
    pub fn new(stream: TcpStream) -> NewResult {
        // Set fd as non-blocking
        let fd = stream.as_raw_fd();
        let mut response;
        unsafe {
            response = libc::fcntl(
                fd,
                libc::consts::os::posix01::F_SETFL,
                libc::consts::os::extra::O_NONBLOCK);
        }

        if response < 0 {
            let errno = os::errno();
            return match errno {
                posix88::EACCES     => Err(SetFdError::EACCES),
                posix88::EAGAIN     => Err(SetFdError::EAGAIN),
                posix88::EBADF      => Err(SetFdError::EBADF),
                posix88::EDEADLK    => Err(SetFdError::EDEADLK),
                posix88::EFAULT     => Err(SetFdError::EFAULT),
                posix88::EINTR      => Err(SetFdError::EINTR),
                posix88::EINVAL     => Err(SetFdError::EINVAL),
                posix88::EMFILE     => Err(SetFdError::EMFILE),
                posix88::ENOLCK     => Err(SetFdError::ENOLCK),
                posix88::EPERM      => Err(SetFdError::EPERM),
                _ => panic!("Unknown errno from fcntl: {}", errno)
            };
        }

        Ok(WebSocketStream {
            stream: stream
        })
    }

    /// Attempts to read data from the socket.
    ///
    /// If data is available, it waits until a complete message has
    /// been received.
    /// It will return immediately if no data is available, or data is present,
    /// but a complete message is not yet available.  The previously read
    /// sections of that message will be discarded.  Non-blocking, ftw!
    pub fn read(&mut self) -> ReadResult {
        match self.check_for_data() {
            Ok(buf) => {
                if buf.len() > 0 {
                    // Ensure opcode is valid
                    let op_code = buf[0] & OP_CODE_UN_MASK;
                    let valid_op = match op_code {
                        OP_CONTINUATION => true,
                        OP_TEXT         => true,
                        OP_BINARY       => true,
                        OP_CLOSE        => true,
                        OP_PING         => true,
                        OP_PONG         => true,
                        _ => false
                    };
                    if !valid_op {
                        return Err(ReadError::OpCode);
                    }

                    // Assign OpCode
                    let op = match op_code {
                        OP_CONTINUATION => OpCode::Continuation,
                        OP_TEXT         => OpCode::Text,
                        OP_BINARY       => OpCode::Binary,
                        OP_CLOSE        => OpCode::Close,
                        OP_PING         => OpCode::Ping,
                        OP_PONG         => OpCode::Pong,
                        _ => unimplemented!()
                    };

                    // Get payload
                    match self.read_payload() {
                        Ok(payload) => Ok((op, payload)),
                        Err(e) => Err(e)
                    }
                } else {
                    Err(ReadError::NoData)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn check_for_data(&mut self) -> SysReadResult {
        self.read_num_bytes(1)
    }

    fn read_payload(&mut self) -> SysReadResult {
        match self.read_payload_key() {
            Ok(key) => {
                match self.read_payload_len(key) {
                    Ok(len) => {
                        match self.read_masking_key() {
                            Ok(mask) => {
                                match self.read_num_bytes(len as usize) {
                                    Ok(buf) => {
                                        if buf.len() < 1 {
                                            return Err(ReadError::DataStop);
                                        }

                                        let mut payload = Vec::<u8>::
                                            with_capacity(buf.len());
                                        for x in 0..buf.len() {
                                            payload.push(buf[x] ^ mask[x % 4]);
                                        }
                                        Ok(payload)
                                    }
                                    Err(e) => Err(e)
                                }
                            }
                            Err(e) => Err(e)
                        }
                    }
                    Err(e) => Err(e)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_payload_key(&mut self) -> PayloadKeyResult {
        match self.read_num_bytes(1) {
            Ok(key_buf) => {
                if key_buf.len() > 0 {
                    Ok(key_buf[0] & PAYLOAD_KEY_UN_MASK)
                } else {
                    Err(ReadError::DataStop)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_payload_len(&mut self, key: u8) -> PayloadLenResult {
        match key {
            126 => {
                match self.read_num_bytes(2) {
                    Ok(buf) => {
                        let mut len = 0u16;
                        len = len | buf[0] as u16;
                        len = (len << 8) ^ buf[1] as u16;
                        Ok(len as u64)
                    }
                    Err(e) => Err(e)
                }
            }
            127 => {
                match self.read_num_bytes(8) {
                    Ok(buf) => {
                        let mut len = 0u64;
                        len = len | buf[0] as u64;
                        len = (len << 8) ^ buf[1] as u64;
                        len = (len << 8) ^ buf[2] as u64;
                        len = (len << 8) ^ buf[3] as u64;
                        len = (len << 8) ^ buf[4] as u64;
                        len = (len << 8) ^ buf[5] as u64;
                        len = (len << 8) ^ buf[6] as u64;
                        len = (len << 8) ^ buf[7] as u64;
                        Ok(len)
                    }
                    Err(e) => Err(e)
                }
            }
            _ => Ok(key as u64)
        }
    }

    fn read_masking_key(&mut self) -> SysReadResult {
        match self.read_num_bytes(4) {
            Ok(mask_buf) => {
                if mask_buf.len() > 0 {
                    Ok(mask_buf)
                } else {
                    Err(ReadError::DataStop)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_num_bytes(&mut self, count: usize) -> SysReadResult {
        let mut temp_count = count;
        let mut total_read: usize = 0;
        let mut final_buffer: Vec<u8> = Vec::with_capacity(count);

        while total_read < count {
            let mut num_read;
            let mut buffer = [0u8; 512];
            let fd = self.stream.as_raw_fd();

            unsafe {
                let buf_ptr = buffer.as_mut_ptr();
                let void_buf_ptr: *mut c_void = mem::transmute(buf_ptr);
                num_read = read(fd, void_buf_ptr, temp_count as size_t);
            }

            if num_read > 0 {
                for x in 0..num_read {
                    final_buffer.push(buffer[x as usize]);
                }

                total_read += num_read as usize;
                temp_count -= num_read as usize;
            } else if num_read == 0 {
                return Ok(final_buffer);
            } else {
                let errno = os::errno();
                return match errno {
                    posix88::EBADF      => Err(ReadError::EBADF),
                    posix88::EFAULT     => Err(ReadError::EFAULT),
                    posix88::EINTR      => Err(ReadError::EINTR),
                    posix88::EINVAL     => Err(ReadError::EINVAL),
                    posix88::EIO        => Err(ReadError::EIO),
                    posix88::EISDIR     => Err(ReadError::EISDIR),
                    _ => Ok(Vec::<u8>::new()),
                };
            }
        }
        Ok(final_buffer)
    }

    /// Attempts to write data to the socket
    pub fn write(&mut self, op: OpCode, payload: &mut Vec<u8>) -> WriteResult {
        let mut out_buf: Vec<u8> = Vec::with_capacity(payload.len() + 9);

        self.set_op_code(&op, &mut out_buf);
        self.set_payload_info(payload.len(), &mut out_buf);
        out_buf.append(payload);

        self.write_bytes(&out_buf)
    }

    fn set_op_code(&self, op: &OpCode, buf: &mut Vec<u8>) {
        let op_code = match *op {
            OpCode::Continuation    => OP_CONTINUATION,
            OpCode::Text            => OP_TEXT,
            OpCode::Binary          => OP_BINARY,
            OpCode::Close           => OP_CLOSE,
            OpCode::Ping            => OP_PING,
            OpCode::Pong            => OP_PONG
        };
        buf.push(op_code | OP_CODE_MASK);
    }

    fn set_payload_info(&self, len: usize, buf: &mut Vec<u8>) {
        if len <= 125 {
            buf.push(len as u8);
        } else if len <= 65535 {
            let mut len_buf = [0u8; 2];
            len_buf[0] = (len as u16 & 0b1111_1111u16 << 8) as u8;
            len_buf[1] = (len as u16 & 0b1111_1111 as u16) as u8;

            buf.push(126u8); // 16 bit prelude
            buf.push(len_buf[0]);
            buf.push(len_buf[1]);
        } else {
            let mut len_buf = [0u8; 8];
            len_buf[0] = (len as u64 & 0b1111_1111u64 << 56) as u8;
            len_buf[1] = (len as u64 & 0b1111_1111u64 << 48) as u8;
            len_buf[2] = (len as u64 & 0b1111_1111u64 << 40) as u8;
            len_buf[3] = (len as u64 & 0b1111_1111u64 << 32) as u8;
            len_buf[4] = (len as u64 & 0b1111_1111u64 << 24) as u8;
            len_buf[5] = (len as u64 & 0b1111_1111u64 << 16) as u8;
            len_buf[6] = (len as u64 & 0b1111_1111u64 << 8) as u8;
            len_buf[7] = (len as u64 & 0b1111_1111u64) as u8;

            buf.push(127u8); // 64 bit prelude
            buf.push(len_buf[0]);
            buf.push(len_buf[1]);
            buf.push(len_buf[2]);
            buf.push(len_buf[3]);
            buf.push(len_buf[4]);
            buf.push(len_buf[5]);
            buf.push(len_buf[6]);
            buf.push(len_buf[7]);
        }
    }

    fn write_bytes(&mut self, buf: &Vec<u8>) -> SysWriteResult {
        let buffer = buf.as_slice();
        let fd = self.stream.as_raw_fd();
        let count = buf.len() as size_t;

        let mut num_written;
        unsafe {
            let buff_ptr = buffer.as_ptr();
            let void_buff_ptr: *const c_void = mem::transmute(buff_ptr);
            num_written = write(fd, void_buff_ptr, count);
        }

        if num_written < 0 {
            let errno = os::errno();
            return match errno {
                posix88::EAGAIN     => Err(WriteError::EAGAIN),
                posix88::EBADF      => Err(WriteError::EBADF),
                posix88::EFAULT     => Err(WriteError::EFAULT),
                posix88::EFBIG      => Err(WriteError::EFBIG),
                posix88::EINTR      => Err(WriteError::EINTR),
                posix88::EINVAL     => Err(WriteError::EINVAL),
                posix88::EIO        => Err(WriteError::EIO),
                posix88::ENOSPC     => Err(WriteError::ENOSPC),
                posix88::EPIPE      => Err(WriteError::EPIPE),
                _ => panic!("Unknown errno: {}", errno),
            }
        }
        Ok(num_written as u64)
    }
}

impl Clone for WebSocketStream {
    fn clone(&self) -> WebSocketStream {
        WebSocketStream {
            stream: self.stream.try_clone().unwrap()
        }
    }
}
<commit_msg>Update lib.rs<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Nathan Sizemore <nathanrsizemore@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.

//! WebSocketStream crate

#![feature(libc, net, std_misc, os, collections, core)]

extern crate libc;

use std::mem;
use std::net::TcpStream;
use std::os;
use std::os::unix::prelude::AsRawFd;

use self::libc::consts::os::posix88;
use self::libc::{size_t, c_void, c_int, ssize_t};

use util::*;
pub mod util;

extern "system" {
    fn read(fd: c_int, buffer: *mut c_void, count: size_t) -> ssize_t;
    fn write(fd: c_int, buffer: *const c_void, cout: size_t) -> ssize_t;
}

pub type NewResult = Result<WebSocketStream, SetFdError>;
pub type ReadResult = Result<(OpCode, Vec<u8>), ReadError>;
pub type WriteResult = Result<u64, WriteError>;

type SysReadResult = Result<Vec<u8>, ReadError>;
type SysWriteResult = Result<u64, WriteError>;
type PayloadKeyResult = Result<u8, ReadError>;
type PayloadLenResult = Result<u64, ReadError>;

/// Represents a non-blocking RFC-6455 Protocol stream
pub struct WebSocketStream {
    stream: TcpStream
}

impl WebSocketStream {

    /// Creates a new WebSocket
    pub fn new(stream: TcpStream) -> NewResult {
        // Set fd as non-blocking
        let fd = stream.as_raw_fd();
        let mut response;
        unsafe {
            response = libc::fcntl(
                fd,
                libc::consts::os::posix01::F_SETFL,
                libc::consts::os::extra::O_NONBLOCK);
        }

        if response < 0 {
            let errno = os::errno();
            return match errno {
                posix88::EACCES     => Err(SetFdError::EACCES),
                posix88::EAGAIN     => Err(SetFdError::EAGAIN),
                posix88::EBADF      => Err(SetFdError::EBADF),
                posix88::EDEADLK    => Err(SetFdError::EDEADLK),
                posix88::EFAULT     => Err(SetFdError::EFAULT),
                posix88::EINTR      => Err(SetFdError::EINTR),
                posix88::EINVAL     => Err(SetFdError::EINVAL),
                posix88::EMFILE     => Err(SetFdError::EMFILE),
                posix88::ENOLCK     => Err(SetFdError::ENOLCK),
                posix88::EPERM      => Err(SetFdError::EPERM),
                _ => panic!("Unknown errno from fcntl: {}", errno)
            };
        }

        Ok(WebSocketStream {
            stream: stream
        })
    }

    /// Attempts to read data from the socket.
    ///
    /// If data is available, it waits until a complete message has
    /// been received.
    /// It will return immediately if no data is available, or data is present,
    /// but a complete message is not yet available.  The previously read
    /// sections of that message will be discarded.  Non-blocking, ftw!
    pub fn read(&mut self) -> ReadResult {
        match self.check_for_data() {
            Ok(buf) => {
                if buf.len() > 0 {
                    // Ensure opcode is valid
                    let op_code = buf[0] & OP_CODE_UN_MASK;
                    let valid_op = match op_code {
                        OP_CONTINUATION => true,
                        OP_TEXT         => true,
                        OP_BINARY       => true,
                        OP_CLOSE        => true,
                        OP_PING         => true,
                        OP_PONG         => true,
                        _ => false
                    };
                    if !valid_op {
                        return Err(ReadError::OpCode);
                    }

                    // Assign OpCode
                    let op = match op_code {
                        OP_CONTINUATION => OpCode::Continuation,
                        OP_TEXT         => OpCode::Text,
                        OP_BINARY       => OpCode::Binary,
                        OP_CLOSE        => OpCode::Close,
                        OP_PING         => OpCode::Ping,
                        OP_PONG         => OpCode::Pong,
                        _ => unimplemented!()
                    };

                    // Get payload
                    match self.read_payload() {
                        Ok(payload) => Ok((op, payload)),
                        Err(e) => Err(e)
                    }
                } else {
                    Err(ReadError::NoData)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn check_for_data(&mut self) -> SysReadResult {
        self.read_num_bytes(1)
    }

    fn read_payload(&mut self) -> SysReadResult {
        match self.read_payload_key() {
            Ok(key) => {
                match self.read_payload_len(key) {
                    Ok(len) => {
                        match self.read_masking_key() {
                            Ok(mask) => {
                                match self.read_num_bytes(len as usize) {
                                    Ok(buf) => {
                                        if buf.len() < 1 {
                                            return Err(ReadError::DataStop);
                                        }

                                        let mut payload = Vec::<u8>::
                                            with_capacity(buf.len());
                                        for x in 0..buf.len() {
                                            payload.push(buf[x] ^ mask[x % 4]);
                                        }
                                        Ok(payload)
                                    }
                                    Err(e) => Err(e)
                                }
                            }
                            Err(e) => Err(e)
                        }
                    }
                    Err(e) => Err(e)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_payload_key(&mut self) -> PayloadKeyResult {
        match self.read_num_bytes(1) {
            Ok(key_buf) => {
                if key_buf.len() > 0 {
                    Ok(key_buf[0] & PAYLOAD_KEY_UN_MASK)
                } else {
                    Err(ReadError::DataStop)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_payload_len(&mut self, key: u8) -> PayloadLenResult {
        match key {
            126 => {
                match self.read_num_bytes(2) {
                    Ok(buf) => {
                        let mut len = 0u16;
                        len = len | buf[0] as u16;
                        len = (len << 8) ^ buf[1] as u16;
                        Ok(len as u64)
                    }
                    Err(e) => Err(e)
                }
            }
            127 => {
                match self.read_num_bytes(8) {
                    Ok(buf) => {
                        let mut len = 0u64;
                        len = len | buf[0] as u64;
                        len = (len << 8) ^ buf[1] as u64;
                        len = (len << 8) ^ buf[2] as u64;
                        len = (len << 8) ^ buf[3] as u64;
                        len = (len << 8) ^ buf[4] as u64;
                        len = (len << 8) ^ buf[5] as u64;
                        len = (len << 8) ^ buf[6] as u64;
                        len = (len << 8) ^ buf[7] as u64;
                        Ok(len)
                    }
                    Err(e) => Err(e)
                }
            }
            _ => Ok(key as u64)
        }
    }

    fn read_masking_key(&mut self) -> SysReadResult {
        match self.read_num_bytes(4) {
            Ok(mask_buf) => {
                if mask_buf.len() > 0 {
                    Ok(mask_buf)
                } else {
                    Err(ReadError::DataStop)
                }
            }
            Err(e) => Err(e)
        }
    }

    fn read_num_bytes(&mut self, count: usize) -> SysReadResult {
        let mut temp_count = count;
        let mut total_read: usize = 0;
        let mut final_buffer: Vec<u8> = Vec::with_capacity(count);

        while total_read < count {
            let mut num_read;
            let mut buffer = [0u8; 512];
            let fd = self.stream.as_raw_fd();

            unsafe {
                let buf_ptr = buffer.as_mut_ptr();
                let void_buf_ptr: *mut c_void = mem::transmute(buf_ptr);
                num_read = read(fd, void_buf_ptr, temp_count as size_t);
            }

            if num_read > 0 {
                for x in 0..num_read {
                    final_buffer.push(buffer[x as usize]);
                }

                total_read += num_read as usize;
                temp_count -= num_read as usize;
            } else if num_read == 0 && total_read == 0 {
                return Ok(final_buffer);
            } else {
                let errno = os::errno();
                return match errno {
                    posix88::EBADF      => Err(ReadError::EBADF),
                    posix88::EFAULT     => Err(ReadError::EFAULT),
                    posix88::EINTR      => Err(ReadError::EINTR),
                    posix88::EINVAL     => Err(ReadError::EINVAL),
                    posix88::EIO        => Err(ReadError::EIO),
                    posix88::EISDIR     => Err(ReadError::EISDIR),
                    _ => Ok(Vec::<u8>::new()),
                };
            }
        }
        Ok(final_buffer)
    }

    /// Attempts to write data to the socket
    pub fn write(&mut self, op: OpCode, payload: &mut Vec<u8>) -> WriteResult {
        let mut out_buf: Vec<u8> = Vec::with_capacity(payload.len() + 9);

        self.set_op_code(&op, &mut out_buf);
        self.set_payload_info(payload.len(), &mut out_buf);
        out_buf.append(payload);

        self.write_bytes(&out_buf)
    }

    fn set_op_code(&self, op: &OpCode, buf: &mut Vec<u8>) {
        let op_code = match *op {
            OpCode::Continuation    => OP_CONTINUATION,
            OpCode::Text            => OP_TEXT,
            OpCode::Binary          => OP_BINARY,
            OpCode::Close           => OP_CLOSE,
            OpCode::Ping            => OP_PING,
            OpCode::Pong            => OP_PONG
        };
        buf.push(op_code | OP_CODE_MASK);
    }

    fn set_payload_info(&self, len: usize, buf: &mut Vec<u8>) {
        if len <= 125 {
            buf.push(len as u8);
        } else if len <= 65535 {
            let mut len_buf = [0u8; 2];
            len_buf[0] = (len as u16 & 0b1111_1111u16 << 8) as u8;
            len_buf[1] = (len as u16 & 0b1111_1111 as u16) as u8;

            buf.push(126u8); // 16 bit prelude
            buf.push(len_buf[0]);
            buf.push(len_buf[1]);
        } else {
            let mut len_buf = [0u8; 8];
            len_buf[0] = (len as u64 & 0b1111_1111u64 << 56) as u8;
            len_buf[1] = (len as u64 & 0b1111_1111u64 << 48) as u8;
            len_buf[2] = (len as u64 & 0b1111_1111u64 << 40) as u8;
            len_buf[3] = (len as u64 & 0b1111_1111u64 << 32) as u8;
            len_buf[4] = (len as u64 & 0b1111_1111u64 << 24) as u8;
            len_buf[5] = (len as u64 & 0b1111_1111u64 << 16) as u8;
            len_buf[6] = (len as u64 & 0b1111_1111u64 << 8) as u8;
            len_buf[7] = (len as u64 & 0b1111_1111u64) as u8;

            buf.push(127u8); // 64 bit prelude
            buf.push(len_buf[0]);
            buf.push(len_buf[1]);
            buf.push(len_buf[2]);
            buf.push(len_buf[3]);
            buf.push(len_buf[4]);
            buf.push(len_buf[5]);
            buf.push(len_buf[6]);
            buf.push(len_buf[7]);
        }
    }

    fn write_bytes(&mut self, buf: &Vec<u8>) -> SysWriteResult {
        let buffer = buf.as_slice();
        let fd = self.stream.as_raw_fd();
        let count = buf.len() as size_t;

        let mut num_written;
        unsafe {
            let buff_ptr = buffer.as_ptr();
            let void_buff_ptr: *const c_void = mem::transmute(buff_ptr);
            num_written = write(fd, void_buff_ptr, count);
        }

        if num_written < 0 {
            let errno = os::errno();
            return match errno {
                posix88::EAGAIN     => Err(WriteError::EAGAIN),
                posix88::EBADF      => Err(WriteError::EBADF),
                posix88::EFAULT     => Err(WriteError::EFAULT),
                posix88::EFBIG      => Err(WriteError::EFBIG),
                posix88::EINTR      => Err(WriteError::EINTR),
                posix88::EINVAL     => Err(WriteError::EINVAL),
                posix88::EIO        => Err(WriteError::EIO),
                posix88::ENOSPC     => Err(WriteError::ENOSPC),
                posix88::EPIPE      => Err(WriteError::EPIPE),
                _ => panic!("Unknown errno: {}", errno),
            }
        }
        Ok(num_written as u64)
    }
}

impl Clone for WebSocketStream {
    fn clone(&self) -> WebSocketStream {
        WebSocketStream {
            stream: self.stream.try_clone().unwrap()
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
use libc::{c_char, c_int, size_t};
use libc::types::common::c95::FILE;

use std::ffi::CString;
use std::string::as_string;
use std::str::from_utf8_unchecked;

#[repr(C)]
pub struct GetLine;
pub struct ExpandFile;
pub struct FileExpansion {
    exists: c_int,
    nfile: c_int,
    files: *mut char,
}
pub struct WordCompletion;

#[link(name = "tecla")]
extern {
    fn libtecla_version(major: *mut c_int, minor: *mut c_int, micro: *mut c_int);
    fn new_GetLine(linelen: size_t, histlen: size_t) -> *mut GetLine;
    fn del_GetLine(gl: *mut GetLine) -> *mut GetLine;
    fn gl_get_line(gl: *mut GetLine,
                   prompt: *const c_char,
                   start_line: *const c_char,
                   start_pos: c_int) -> *const c_char;
    fn gl_query_char(gl: *mut GetLine, prompt: *const c_char, defchar: c_char) -> c_int;
    fn gl_read_char(gl: *mut GetLine) -> c_int;
    fn gl_configure_getline(gl: *mut GetLine, app_string: *const c_char,
                            app_file: *const c_char, user_file: *const c_char) -> c_int;
    // fn gl_bind_keyseq() -> c_int;
    fn new_ExpandFile() -> *mut ExpandFile;
    fn del_ExpandFile(ef: *mut ExpandFile) -> *mut ExpandFile;
    fn ef_expand_file(ef: *mut ExpandFile,
                      path: *const c_char,
                      pathlen: c_int) -> *mut FileExpansion;
    fn el_list_expansions(result: *mut FileExpansion,
                          fp: *mut FILE,
                          term_width: c_int) -> c_int;
    fn ef_last_error(ef: *mut ExpandFile) -> *const c_char;
    fn new_WordCompletion() -> *mut WordCompletion;
    fn del_WordCompletion(cpl: *mut WordCompletion) -> WordCompletion;
}

pub fn new_gl(linelen: usize, histlen: usize) -> *mut GetLine {
    let line = linelen as size_t;
    let hist = histlen as size_t;
    let mut res: *mut GetLine;
    unsafe {
        res = new_GetLine(line, hist);
    }
    res
}

pub fn get_line(gl: *mut GetLine, prompt: &str) -> String {
    let c_prompt = CString::from_slice(prompt.as_bytes());
    let mut out: *const c_char;
    let start = 0 as i8;
    let mut res: &str;
    unsafe {
        out = gl_get_line(gl, c_prompt.as_ptr(), &start, -1);
        res = from_utf8_unchecked(std::ffi::c_str_to_bytes(&out));
    }
    as_string(res).clone()
}

#[test]
fn it_works() {
}
<commit_msg>The library is complete enough to allow the example shell to read a line and tab complete filenames. Gonna call that a win for a while<commit_after>#![feature(libc)]
#![feature(collections)]
#![feature(std_misc)]
extern crate libc;
use libc::{c_char, c_int, size_t};
//use libc::types::common::c95::FILE;

use std::ffi::{CString, CStr};
use std::string::as_string;
use std::str::from_utf8_unchecked;
use std::ptr;

#[repr(C)]
pub struct GetLine;
/*pub struct ExpandFile;
pub struct FileExpansion {
    exists: c_int,
    nfile: c_int,
    files: *mut char,
}
pub struct WordCompletion;
*/
// will add functions as needed
#[link(name = "tecla")]
extern {
    // fn libtecla_version(major: *mut c_int, minor: *mut c_int, micro: *mut c_int);
    fn new_GetLine(linelen: size_t, histlen: size_t) -> *mut GetLine;
    // fn del_GetLine(gl: *mut GetLine) -> *mut GetLine;
    fn gl_get_line(gl: *mut GetLine,
                   prompt: *const c_char,
                   start_line: *const c_char,
                   start_pos: c_int) -> *const c_char;
    // fn gl_query_char(gl: *mut GetLine, prompt: *const c_char, defchar: c_char) -> c_int;
    // fn gl_read_char(gl: *mut GetLine) -> c_int;
    // fn gl_configure_getline(gl: *mut GetLine, app_string: *const c_char,
    //                        app_file: *const c_char, user_file: *const c_char) -> c_int;
    // fn gl_bind_keyseq() -> c_int;
    // fn new_ExpandFile() -> *mut ExpandFile;
    // fn del_ExpandFile(ef: *mut ExpandFile) -> *mut ExpandFile;
    // fn ef_expand_file(ef: *mut ExpandFile,
    //                  path: *const c_char,
    //                  pathlen: c_int) -> *mut FileExpansion;
    // fn el_list_expansions(result: *mut FileExpansion,
    //                      fp: *mut FILE,
    //                      term_width: c_int) -> c_int;
    // fn ef_last_error(ef: *mut ExpandFile) -> *const c_char;
    // fn new_WordCompletion() -> *mut WordCompletion;
    // fn del_WordCompletion(cpl: *mut WordCompletion) -> WordCompletion;
}

pub fn new_gl(linelen: usize, histlen: usize) -> *mut GetLine {
    let line = linelen as size_t;
    let hist = histlen as size_t;
    let mut res: *mut GetLine;
    unsafe {
        res = new_GetLine(line, hist);
    }
    res
}

pub fn get_line(gl: *mut GetLine, prompt: &str) -> String {
    let c_prompt = CString::new(prompt.as_bytes()).unwrap();
    let start: *const i8 = ptr::null();
    let mut res: &str;
    unsafe {
        let out = CStr::from_ptr(gl_get_line(gl, c_prompt.as_ptr(), start, -1));
        res = from_utf8_unchecked(out.to_bytes());
    }
    as_string(res).clone()
}

#[test]
fn it_works() {
}
<|endoftext|>