<commit_before>extern crate libc;
extern crate regex;

use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io::{self, Write};
use std::ops::Deref;
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
use std::slice;
use std::str;

use libc::{abort, c_char, size_t};
use regex::bytes;

macro_rules! ffi_fn {
    (fn $name:ident($($arg:ident: $arg_ty:ty),*,) -> $ret:ty $body:block) => {
        ffi_fn!(fn $name($($arg: $arg_ty),*) -> $ret $body);
    };
    (fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block) => {
        #[no_mangle]
        pub extern fn $name($($arg: $arg_ty),*) -> $ret {
            match panic::catch_unwind(AssertUnwindSafe(move || $body)) {
                Ok(v) => v,
                Err(err) => {
                    let msg = if let Some(&s) = err.downcast_ref::<&str>() {
                        s.to_owned()
                    } else if let Some(s) = err.downcast_ref::<String>() {
                        s.to_owned()
                    } else {
                        "UNABLE TO SHOW RESULT OF PANIC.".to_owned()
                    };
                    let _ = writeln!(
                        &mut io::stderr(),
                        "panic unwind caught, aborting: {:?}",
                        msg);
                    unsafe { abort() }
                }
            }
        }
    };
    (fn $name:ident($($arg:ident: $arg_ty:ty),*,) $body:block) => {
        ffi_fn!(fn $name($($arg: $arg_ty),*) -> () $body);
    };
    (fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block) => {
        ffi_fn!(fn $name($($arg: $arg_ty),*) -> () $body);
    };
}

pub struct Regex {
    re: bytes::Regex,
    capture_names: HashMap<String, i32>,
}

pub struct Options {
    size_limit: usize,
    dfa_size_limit: usize,
}

const RURE_FLAG_CASEI: u32 = 1 << 0;
const RURE_FLAG_MULTI: u32 = 1 << 1;
const RURE_FLAG_DOTNL: u32 = 1 << 2;
const RURE_FLAG_SWAP_GREED: u32 = 1 << 3;
const RURE_FLAG_SPACE: u32 = 1 << 4;
const RURE_FLAG_UNICODE: u32 = 1 << 5;
const RURE_DEFAULT_FLAGS: u32 = RURE_FLAG_UNICODE;

#[repr(C)]
pub struct rure_match {
    pub start: size_t,
    pub end: size_t,
}

pub struct Captures(Vec<Option<usize>>);

pub struct Iter {
    re: *const Regex,
    last_end: usize,
    last_match: Option<usize>,
}

#[derive(Debug)]
pub struct Error {
    message: Option<CString>,
    kind: ErrorKind,
}

#[derive(Debug)]
enum ErrorKind {
    None,
    Str(str::Utf8Error),
    Regex(regex::Error),
}

impl Deref for Regex {
    type Target = bytes::Regex;
    fn deref(&self) -> &bytes::Regex { &self.re }
}

impl Default for Options {
    fn default() -> Options {
        Options {
            size_limit: 10 * (1<<20),
            dfa_size_limit: 2 * (1<<20),
        }
    }
}

impl Error {
    fn new(kind: ErrorKind) -> Error {
        Error {
            message: None,
            kind: kind,
        }
    }

    fn is_err(&self) -> bool {
        match self.kind {
            ErrorKind::None => false,
            ErrorKind::Str(_) | ErrorKind::Regex(_) => true,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.kind {
            ErrorKind::None => write!(f, "no error"),
            ErrorKind::Str(ref e) => e.fmt(f),
            ErrorKind::Regex(ref e) => e.fmt(f),
        }
    }
}

ffi_fn! {
    fn rure_compile_must(pattern: *const c_char) -> *const Regex {
        let len = unsafe { CStr::from_ptr(pattern).to_bytes().len() };
        let pat = pattern as *const u8;
        let mut err = Error::new(ErrorKind::None);
        let re = rure_compile(
            pat, len, RURE_DEFAULT_FLAGS, ptr::null(), &mut err);
        if err.is_err() {
            let _ = writeln!(&mut io::stderr(), "{}", err);
            let _ = writeln!(
                &mut io::stderr(), "aborting from rure_compile_must");
            unsafe { abort() }
        }
        re
    }
}

ffi_fn! {
    fn rure_compile(
        pattern: *const u8,
        length: size_t,
        flags: u32,
        options: *const Options,
        error: *mut Error,
    ) -> *const Regex {
        let pat = unsafe { slice::from_raw_parts(pattern, length) };
        let pat = match str::from_utf8(pat) {
            Ok(pat) => pat,
            Err(err) => {
                unsafe {
                    if !error.is_null() {
                        *error = Error::new(ErrorKind::Str(err));
                    }
                    return ptr::null();
                }
            }
        };
        let mut builder = bytes::RegexBuilder::new(pat);
        if !options.is_null() {
            let options = unsafe { &*options };
            builder = builder.size_limit(options.size_limit);
            builder = builder.dfa_size_limit(options.dfa_size_limit);
        }
        builder = builder.case_insensitive(flags & RURE_FLAG_CASEI > 0);
        builder = builder.multi_line(flags & RURE_FLAG_MULTI > 0);
        builder = builder.dot_matches_new_line(flags & RURE_FLAG_DOTNL > 0);
        builder = builder.swap_greed(flags & RURE_FLAG_SWAP_GREED > 0);
        builder = builder.ignore_whitespace(flags & RURE_FLAG_SPACE > 0);
        builder = builder.unicode(flags & RURE_FLAG_UNICODE > 0);
        match builder.compile() {
            Ok(re) => {
                let mut capture_names = HashMap::new();
                for (i, name) in re.capture_names().enumerate() {
                    if let Some(name) = name {
                        capture_names.insert(name.to_owned(), i as i32);
                    }
                }
                let re = Regex {
                    re: re,
                    capture_names: capture_names,
                };
                Box::into_raw(Box::new(re))
            }
            Err(err) => {
                unsafe {
                    if !error.is_null() {
                        *error = Error::new(ErrorKind::Regex(err));
                    }
                    ptr::null()
                }
            }
        }
    }
}

ffi_fn! {
    fn rure_free(re: *const Regex) {
        unsafe { Box::from_raw(re as *mut Regex); }
    }
}

ffi_fn! {
    fn rure_is_match(
        re: *const Regex,
        haystack: *const u8,
        len: size_t,
        start: size_t,
    ) -> bool {
        let re = unsafe { &*re };
        let haystack = unsafe { slice::from_raw_parts(haystack, len) };
        re.is_match_at(haystack, start)
    }
}

ffi_fn! {
    fn rure_find(
        re: *const Regex,
        haystack: *const u8,
        len: size_t,
        start: size_t,
        match_info: *mut rure_match,
    ) -> bool {
        let re = unsafe { &*re };
        let haystack = unsafe { slice::from_raw_parts(haystack, len) };
        re.find_at(haystack, start).map(|(s, e)| unsafe {
            if !match_info.is_null() {
                (*match_info).start = s;
                (*match_info).end = e;
            }
        }).is_some()
    }
}

ffi_fn! {
    fn rure_find_captures(
        re: *const Regex,
        haystack: *const u8,
        len: size_t,
        start: size_t,
        captures: *mut Captures,
    ) -> bool {
        let re = unsafe { &*re };
        let haystack = unsafe { slice::from_raw_parts(haystack, len) };
        let slots = unsafe { &mut (*captures).0 };
        re.read_captures_at(slots, haystack, start).is_some()
    }
}

ffi_fn! {
    fn rure_shortest_match(
        re: *const Regex,
        haystack: *const u8,
        len: size_t,
        start: size_t,
        end: *mut usize,
    ) -> bool {
        let re = unsafe { &*re };
        let haystack = unsafe { slice::from_raw_parts(haystack, len) };
        match re.shortest_match_at(haystack, start) {
            None => false,
            Some(i) => {
                if !end.is_null() {
                    unsafe {
                        *end = i;
                    }
                }
                true
            }
        }
    }
}

ffi_fn! {
    fn rure_capture_name_index(
        re: *const Regex,
        name: *const c_char,
    ) -> i32 {
        let re = unsafe { &*re };
        let name = unsafe { CStr::from_ptr(name) };
        let name = match name.to_str() {
            Err(_) => return -1,
            Ok(name) => name,
        };
        re.capture_names.get(name).map(|&i|i).unwrap_or(-1)
    }
}

ffi_fn! {
    fn rure_iter_new(
        re: *const Regex,
    ) -> *mut Iter {
        Box::into_raw(Box::new(Iter {
            re: re,
            last_end: 0,
            last_match: None,
        }))
    }
}

ffi_fn! {
    fn rure_iter_free(it: *mut Iter) {
        unsafe { Box::from_raw(it); }
    }
}

ffi_fn! {
    fn rure_iter_next(
        it: *mut Iter,
        haystack: *const u8,
        len: size_t,
        match_info: *mut rure_match,
    ) -> bool {
        let it = unsafe { &mut *it };
        let re = unsafe { &*it.re };
        let text = unsafe { slice::from_raw_parts(haystack, len) };
        if it.last_end > text.len() {
            return false;
        }
        let (s, e) = match re.find_at(text, it.last_end) {
            None => return false,
            Some((s, e)) => (s, e),
        };
        if s == e {
            // This is an empty match. To ensure we make progress, start
            // the next search at the smallest possible starting position
            // of the next match following this one.
            it.last_end += 1;
            // Don't accept empty matches immediately following a match.
            // Just move on to the next match.
            if Some(e) == it.last_match {
                return rure_iter_next(it, haystack, len, match_info);
            }
        } else {
            it.last_end = e;
        }
        it.last_match = Some(e);
        if !match_info.is_null() {
            unsafe {
                (*match_info).start = s;
                (*match_info).end = e;
            }
        }
        true
    }
}

ffi_fn! {
    fn rure_iter_next_captures(
        it: *mut Iter,
        haystack: *const u8,
        len: size_t,
        captures: *mut Captures,
    ) -> bool {
        let it = unsafe { &mut *it };
        let re = unsafe { &*it.re };
        let slots = unsafe { &mut (*captures).0 };
        let text = unsafe { slice::from_raw_parts(haystack, len) };
        if it.last_end > text.len() {
            return false;
        }
        let (s, e) = match re.read_captures_at(slots, text, it.last_end) {
            None => return false,
            Some((s, e)) => (s, e),
        };
        if s == e {
            // This is an empty match. To ensure we make progress, start
            // the next search at the smallest possible starting position
            // of the next match following this one.
            it.last_end += 1;
            // Don't accept empty matches immediately following a match.
            // Just move on to the next match.
            if Some(e) == it.last_match {
                return rure_iter_next_captures(it, haystack, len, captures);
            }
        } else {
            it.last_end = e;
        }
        it.last_match = Some(e);
        true
    }
}

ffi_fn! {
    fn rure_captures_new(re: *const Regex) -> *mut Captures {
        let re = unsafe { &*re };
        let captures = Captures(vec![None; 2 * re.captures_len()]);
        Box::into_raw(Box::new(captures))
    }
}

ffi_fn! {
    fn rure_captures_free(captures: *const Captures) {
        unsafe { Box::from_raw(captures as *mut Captures); }
    }
}

ffi_fn! {
    fn rure_captures_at(
        captures: *const Captures,
        i: size_t,
        match_info: *mut rure_match,
    ) -> bool {
        let captures = unsafe { &(*captures).0 };
        match (captures[i * 2], captures[i * 2 + 1]) {
            (Some(start), Some(end)) => {
                if !match_info.is_null() {
                    unsafe {
                        (*match_info).start = start;
                        (*match_info).end = end;
                    }
                }
                true
            }
            _ => false
        }
    }
}

ffi_fn! {
    fn rure_captures_len(captures: *const Captures) -> size_t {
        unsafe { (*captures).0.len() / 2 }
    }
}

ffi_fn! {
    fn rure_options_new() -> *mut Options {
        Box::into_raw(Box::new(Options::default()))
    }
}

ffi_fn! {
    fn rure_options_free(options: *mut Options) {
        unsafe { Box::from_raw(options); }
    }
}

ffi_fn! {
    fn rure_options_size_limit(options: *mut Options, limit: size_t) {
        let options = unsafe { &mut *options };
        options.size_limit = limit;
    }
}

ffi_fn! {
    fn rure_options_dfa_size_limit(options: *mut Options, limit: size_t) {
        let options = unsafe { &mut *options };
        options.dfa_size_limit = limit;
    }
}

ffi_fn! {
    fn rure_error_new() -> *mut Error {
        Box::into_raw(Box::new(Error::new(ErrorKind::None)))
    }
}

ffi_fn! {
    fn rure_error_free(err: *mut Error) {
        unsafe { Box::from_raw(err); }
    }
}

ffi_fn! {
    fn rure_error_message(err: *mut Error) -> *const c_char {
        let err = unsafe { &mut *err };
        let cmsg = match CString::new(format!("{}", err)) {
            Ok(msg) => msg,
            Err(err) => {
                // I guess this can probably happen if the regex itself has a
                // NUL, and that NUL re-occurs in the context presented by the
                // error message. In this case, just show as much as we can.
                let nul = err.nul_position();
                let msg = err.into_vec();
                CString::new(msg[0..nul].to_owned()).unwrap()
            }
        };
        let p = cmsg.as_ptr();
        err.message = Some(cmsg);
        p
    }
}
<commit_msg>split C api into modules<commit_after>extern crate libc;
extern crate regex;

#[macro_use]
mod macros;
mod rure;
mod error;

pub use rure::*;
pub use error::*;
<|endoftext|>


<commit_before>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::{OpenOptions, remove_file};
use std::io::{BufRead, BufReader, Write};
use std::collections::HashMap;
use std::ops::Deref;
use strip;
use utils::{join, loop_over_files};

use stripper_interface::{
    TypeStruct,
    EventType,
    Type,
    MOD_COMMENT,
    FILE_COMMENT,
    FILE,
};
use types::OUTPUT_COMMENT_FILE;

fn get_corresponding_type(elements: &[(Option<TypeStruct>, Vec<String>)],
                          to_find: &Option<TypeStruct>,
                          mut line: usize,
                          decal: &mut usize,
                          original_content: &mut Vec<String>) -> Option<usize> {
    let mut pos = 0;

    while pos < elements.len() {
        if match (&elements[pos].0, to_find) {
            (&Some(ref a), &Some(ref b)) => {
                let ret = a == b;

                // to detect variants
                if !ret && b.ty == Type::Unknown && b.parent.is_some() && a.parent.is_some() && a.parent == b.parent {
                    if match b.parent {
                        Some(ref p) => p.ty == Type::Struct || p.ty == Type::Enum || p.ty == Type::Use,
                        None => false,
                    } {
                        let mut tmp = b.clone();

                        tmp.ty = Type::Variant;
                        a == &tmp
                    } else {
                        false
                    }
                } else {
                    ret
                }
            },
            _ => false,
        } {
            let mut file_comment = false;

            if elements[pos].1.len() > 0 && elements[pos].1[0].starts_with(FILE_COMMENT) {
                line += 1;
                file_comment = true;
            } else {
                while line > 0 && (line + *decal) > 0 &&
                      original_content[line + *decal - 1].trim().starts_with("#") {
                    line -= 1;
                }
            }
            for comment in &elements[pos].1 {
                if file_comment {
                    original_content.insert(line + *decal, comment[FILE_COMMENT.len()..].to_owned());
                } else {
                    original_content.insert(line + *decal, comment.clone());
                }
                *decal += 1;
            }
            return Some(pos);
        }
        pos += 1;
    }
    None
}

fn regenerate_comments(path: &str, infos: &mut HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    if !infos.contains_key(path) {
        return;
    }
    match strip::build_event_list(path) {
        Ok(mut parse_result) => {
            let mut elements = infos.get_mut(path).unwrap();
            let mut position = 0;
            let mut decal = 0;

            // first, we need to put back file comment
            for entry in elements.iter() {
                if entry.0.is_none() {
                    let mut it = 0;

                    while it < parse_result.original_content.len() {
                        if parse_result.original_content[it].starts_with("/") &&
                           it + 1 < parse_result.original_content.len() &&
                           parse_result.original_content[it + 1].len() < 1 {
                            it += 2;
                            break;
                        }
                        it += 1;
                    }
                    if it < parse_result.original_content.len() {
                        for line in &entry.1 {
                            parse_result.original_content.insert(it, line.clone());
                            decal += 1;
                            it += 1;
                        }
                    }
                    break;
                }
                position += 1;
            }
            if position < elements.len() {
                elements.remove(position);
            }
            let mut waiting_type = None;
            let mut current = None;
            let mut it = 0;

            while it < parse_result.event_list.len() {
                match parse_result.event_list[it].event {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            let tmp = strip::add_to_type_scope(&current, &waiting_type);

                            match get_corresponding_type(&elements, &tmp,
                                                         parse_result.event_list[it].line,
                                                         &mut decal,
                                                         &mut parse_result.original_content) {
                                Some(l) => { elements.remove(l); },
                                None => {}
                            };
                        } else {
                            match current {
                                Some(ref c) => {
                                    if c.ty == Type::Struct || c.ty == Type::Enum ||
                                       c.ty == Type::Mod {
                                        let tmp = Some(t.clone());
                                        let cc = strip::add_to_type_scope(&current, &tmp);

                                        match get_corresponding_type(&elements, &cc,
                                                                     parse_result.event_list[it].line,
                                                                     &mut decal,
                                                                     &mut parse_result.original_content) {
                                            Some(l) => { elements.remove(l); },
                                            None => {}
                                        }
                                    }
                                }
                                None => {}
                            }
                        }
                    }
                    EventType::InScope => {
                        current = strip::add_to_type_scope(&current, &waiting_type);
                        waiting_type = None;
                        match get_corresponding_type(&elements, &current,
                                                     parse_result.event_list[it].line,
                                                     &mut decal,
                                                     &mut parse_result.original_content) {
                            Some(l) => { elements.remove(l); },
                            None => {}
                        };
                    }
                    EventType::OutScope => {
                        current = strip::type_out_scope(&current);
                        waiting_type = None;
                    }
                    _ => {}
                }
                it += 1;
            }
            rewrite_file(path, &parse_result.original_content);
        }
        Err(e) => {
            println!("Error on file '{}': {}", path, e);
        }
    }
}

fn rewrite_file(path: &str, o_content: &[String]) {
    match OpenOptions::new().write(true).create(true).truncate(true).open(path) {
        Ok(mut f) => {
            write!(f, "{}", o_content.join("\n")).unwrap();
        }
        Err(e) => {
            println!("Cannot open '{}': {}", path, e);
        }
    }
}

fn parse_mod_line(line: &str) -> Option<TypeStruct> {
    let line = line.replace(MOD_COMMENT, "");
    let parts : Vec<&str> = line.split("§").collect();
    let mut current = None;

    for part in parts {
        let elems : Vec<&str> = part.split(" ").filter(|x| x.len() > 0).collect();

        current = strip::add_to_type_scope(&current.clone(),
                                           &Some(TypeStruct::new(Type::from(elems[0]),
                                                                 elems[elems.len() - 1])));
    }
    current
}

fn save_remainings(infos: &HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    let mut remainings = 0;

    for (_, content) in infos {
        if content.len() > 0 {
            remainings += 1;
        }
    }
    if remainings < 1 {
        remove_file(OUTPUT_COMMENT_FILE);
        return;
    }
    match OpenOptions::new().write(true).create(true).truncate(true).open(OUTPUT_COMMENT_FILE) {
        Ok(mut out_file) => {
            println!("Some comments couldn't have been regenerated to the files. Saving them back to '{}'.",
                     OUTPUT_COMMENT_FILE);
            for (key, content) in infos {
                if content.len() < 1 {
                    continue;
                }
                writeln!(out_file, "{}{}", FILE_COMMENT, key);
                for line in content {
                    match line.0 {
                        Some(ref d) => {
                            writeln!(out_file, "{}{}\n{}", MOD_COMMENT, d, join(&line.1, "\n"));
                        }
                        None => {}
                    }
                }
            }
            println!("Not implemented yet.");
        }
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    }
}

pub fn regenerate_doc_comments(directory: &str, verbose: bool) {
    // we start by storing files info
    let f = match OpenOptions::new().read(true).open(OUTPUT_COMMENT_FILE) {
        Ok(f) => f,
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    };
    let reader = BufReader::new(f);
    let lines: Vec<String> = reader.lines().collect::<Result<_, _>>().unwrap();
    regenerate_doc_comments_real(directory, verbose, &lines);
}

pub fn regenerate_doc_comments_real<S>(directory: &str, verbose: bool, lines: &[S])
where S: Deref<Target = str> {
    let mut current_file = String::new();
    let mut infos = HashMap::new();
    let mut current_infos = vec!();
    let mut it = 0;

    while it < lines.len() {
        if lines[it].starts_with(FILE) {
            if current_file.len() > 0 && current_infos.len() > 0 {
                infos.insert(current_file, current_infos.clone());
                current_infos = vec!();
            }
            current_file = lines[it].replace(FILE, "").to_owned();
            it += 1;
        } else if lines[it].starts_with(MOD_COMMENT) {
            let ty = parse_mod_line(&lines[it]);
            let mut comments = vec!();

            it += 1;
            if ty.is_none() {
                continue;
            }
            while it < lines.len() {
                if lines[it].starts_with(MOD_COMMENT) ||
                   lines[it].starts_with(FILE) {
                    break;
                }
                comments.push(lines[it].to_owned());
                it += 1;
            }
            if comments.len() > 0 {
                current_infos.push((ty, comments));
            }
        } else if lines[it].starts_with(FILE_COMMENT) {
            let mut comment_lines = vec!();

            while it < lines.len() && lines[it].starts_with(FILE_COMMENT) {
                comment_lines.push(lines[it][FILE_COMMENT.len()..].to_owned());
                it += 1;
            }
            current_infos.push((None, comment_lines));
        }
    }
    if current_file.len() > 0 && current_infos.len() > 0 {
        infos.insert(current_file, current_infos.clone());
    }
    loop_over_files(directory, &mut infos, &regenerate_comments, &vec!(), verbose);
    save_remainings(&infos);
    // TODO: rewrite comments.cmts with remaining infos in regenerate_comments
}<commit_msg>Rustify the parser a little<commit_after>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::{OpenOptions, remove_file};
use std::io::{BufRead, BufReader, Write};
use std::collections::HashMap;
use std::mem;
use std::ops::Deref;
use strip;
use utils::{join, loop_over_files};

use stripper_interface::{
    TypeStruct,
    EventType,
    Type,
    MOD_COMMENT,
    FILE_COMMENT,
    FILE,
};
use types::OUTPUT_COMMENT_FILE;

fn get_corresponding_type(elements: &[(Option<TypeStruct>, Vec<String>)],
                          to_find: &Option<TypeStruct>,
                          mut line: usize,
                          decal: &mut usize,
                          original_content: &mut Vec<String>) -> Option<usize> {
    let mut pos = 0;

    while pos < elements.len() {
        if match (&elements[pos].0, to_find) {
            (&Some(ref a), &Some(ref b)) => {
                let ret = a == b;

                // to detect variants
                if !ret && b.ty == Type::Unknown && b.parent.is_some() && a.parent.is_some() && a.parent == b.parent {
                    if match b.parent {
                        Some(ref p) => p.ty == Type::Struct || p.ty == Type::Enum || p.ty == Type::Use,
                        None => false,
                    } {
                        let mut tmp = b.clone();

                        tmp.ty = Type::Variant;
                        a == &tmp
                    } else {
                        false
                    }
                } else {
                    ret
                }
            },
            _ => false,
        } {
            let mut file_comment = false;

            if elements[pos].1.len() > 0 && elements[pos].1[0].starts_with(FILE_COMMENT) {
                line += 1;
                file_comment = true;
            } else {
                while line > 0 && (line + *decal) > 0 &&
                      original_content[line + *decal - 1].trim().starts_with("#") {
                    line -= 1;
                }
            }
            for comment in &elements[pos].1 {
                if file_comment {
                    original_content.insert(line + *decal, comment[FILE_COMMENT.len()..].to_owned());
                } else {
                    original_content.insert(line + *decal, comment.clone());
                }
                *decal += 1;
            }
            return Some(pos);
        }
        pos += 1;
    }
    None
}

fn regenerate_comments(path: &str, infos: &mut HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    if !infos.contains_key(path) {
        return;
    }
    match strip::build_event_list(path) {
        Ok(mut parse_result) => {
            let mut elements = infos.get_mut(path).unwrap();
            let mut position = 0;
            let mut decal = 0;

            // first, we need to put back file comment
            for entry in elements.iter() {
                if entry.0.is_none() {
                    let mut it = 0;

                    while it < parse_result.original_content.len() {
                        if parse_result.original_content[it].starts_with("/") &&
                           it + 1 < parse_result.original_content.len() &&
                           parse_result.original_content[it + 1].len() < 1 {
                            it += 2;
                            break;
                        }
                        it += 1;
                    }
                    if it < parse_result.original_content.len() {
                        for line in &entry.1 {
                            parse_result.original_content.insert(it, line.clone());
                            decal += 1;
                            it += 1;
                        }
                    }
                    break;
                }
                position += 1;
            }
            if position < elements.len() {
                elements.remove(position);
            }
            let mut waiting_type = None;
            let mut current = None;
            let mut it = 0;

            while it < parse_result.event_list.len() {
                match parse_result.event_list[it].event {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            let tmp = strip::add_to_type_scope(&current, &waiting_type);

                            match get_corresponding_type(&elements, &tmp,
                                                         parse_result.event_list[it].line,
                                                         &mut decal,
                                                         &mut parse_result.original_content) {
                                Some(l) => { elements.remove(l); },
                                None => {}
                            };
                        } else {
                            match current {
                                Some(ref c) => {
                                    if c.ty == Type::Struct || c.ty == Type::Enum ||
                                       c.ty == Type::Mod {
                                        let tmp = Some(t.clone());
                                        let cc = strip::add_to_type_scope(&current, &tmp);

                                        match get_corresponding_type(&elements, &cc,
                                                                     parse_result.event_list[it].line,
                                                                     &mut decal,
                                                                     &mut parse_result.original_content) {
                                            Some(l) => { elements.remove(l); },
                                            None => {}
                                        }
                                    }
                                }
                                None => {}
                            }
                        }
                    }
                    EventType::InScope => {
                        current = strip::add_to_type_scope(&current, &waiting_type);
                        waiting_type = None;
                        match get_corresponding_type(&elements, &current,
                                                     parse_result.event_list[it].line,
                                                     &mut decal,
                                                     &mut parse_result.original_content) {
                            Some(l) => { elements.remove(l); },
                            None => {}
                        };
                    }
                    EventType::OutScope => {
                        current = strip::type_out_scope(&current);
                        waiting_type = None;
                    }
                    _ => {}
                }
                it += 1;
            }
            rewrite_file(path, &parse_result.original_content);
        }
        Err(e) => {
            println!("Error on file '{}': {}", path, e);
        }
    }
}

fn rewrite_file(path: &str, o_content: &[String]) {
    match OpenOptions::new().write(true).create(true).truncate(true).open(path) {
        Ok(mut f) => {
            write!(f, "{}", o_content.join("\n")).unwrap();
        }
        Err(e) => {
            println!("Cannot open '{}': {}", path, e);
        }
    }
}

fn parse_mod_line(line: &str) -> Option<TypeStruct> {
    let line = line.replace(MOD_COMMENT, "");
    let parts : Vec<&str> = line.split("§").collect();
    let mut current = None;

    for part in parts {
        let elems : Vec<&str> = part.split(" ").filter(|x| x.len() > 0).collect();

        current = strip::add_to_type_scope(&current.clone(),
                                           &Some(TypeStruct::new(Type::from(elems[0]),
                                                                 elems[elems.len() - 1])));
    }
    current
}

fn save_remainings(infos: &HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    let mut remainings = 0;

    for (_, content) in infos {
        if content.len() > 0 {
            remainings += 1;
        }
    }
    if remainings < 1 {
        remove_file(OUTPUT_COMMENT_FILE);
        return;
    }
    match OpenOptions::new().write(true).create(true).truncate(true).open(OUTPUT_COMMENT_FILE) {
        Ok(mut out_file) => {
            println!("Some comments couldn't have been regenerated to the files. Saving them back to '{}'.",
                     OUTPUT_COMMENT_FILE);
            for (key, content) in infos {
                if content.len() < 1 {
                    continue;
                }
                writeln!(out_file, "{}{}", FILE_COMMENT, key);
                for line in content {
                    match line.0 {
                        Some(ref d) => {
                            writeln!(out_file, "{}{}\n{}", MOD_COMMENT, d, join(&line.1, "\n"));
                        }
                        None => {}
                    }
                }
            }
            println!("Not implemented yet.");
        }
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    }
}

pub fn regenerate_doc_comments(directory: &str, verbose: bool) {
    // we start by storing files info
    let f = match OpenOptions::new().read(true).open(OUTPUT_COMMENT_FILE) {
        Ok(f) => f,
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    };
    let reader = BufReader::new(f);
    let lines: Vec<String> = reader.lines().collect::<Result<_, _>>().unwrap();
    regenerate_doc_comments_real(directory, verbose, &lines);
}

pub fn regenerate_doc_comments_real<S>(directory: &str, verbose: bool, lines: &[S])
where S: Deref<Target = str> {
    let mut current_file: Option<String> = None;
    let mut infos: HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>> = HashMap::new();
    let mut current_infos: Vec<(Option<TypeStruct>, Vec<String>)> = vec![];
    let mut it = 0;

    while it < lines.len() {
        if lines[it].starts_with(FILE) {
            if let Some(file) = current_file.take() {
                if !current_infos.is_empty() {
                    infos.insert(file, mem::replace(&mut current_infos, vec![]));
                }
            }
            current_file = Some(lines[it].replace(FILE, "").to_owned());
            it += 1;
        } else if lines[it].starts_with(MOD_COMMENT) {
            let ty = parse_mod_line(&lines[it]);
            let mut comments = vec!();

            it += 1;
            if ty.is_none() {
                continue;
            }
            while it < lines.len() {
                if lines[it].starts_with(MOD_COMMENT) ||
                   lines[it].starts_with(FILE) {
                    break;
                }
                comments.push(lines[it].to_owned());
                it += 1;
            }
            if comments.len() > 0 {
                current_infos.push((ty, comments));
            }
        } else if lines[it].starts_with(FILE_COMMENT) {
            let mut comment_lines = vec!();

            while it < lines.len() && lines[it].starts_with(FILE_COMMENT) {
                comment_lines.push(lines[it][FILE_COMMENT.len()..].to_owned());
                it += 1;
            }
            current_infos.push((None, comment_lines));
        }
    }
    if let Some(file) = current_file.take() {
        if !current_infos.is_empty() {
            infos.insert(file, mem::replace(&mut current_infos, vec![]));
        }
    }
    loop_over_files(directory, &mut infos, &regenerate_comments, &vec!(), verbose);
    save_remainings(&infos);
    // TODO: rewrite comments.cmts with remaining infos in regenerate_comments
}
<|endoftext|>


<commit_before>
pub struct TwitchClient {
	user: String,
	auth: String,
	default_channel: String,
	logging: bool,
}

impl TwitchClient {
	pub fn connect(&self) {
		// TODO: Connect!
	}
}
<commit_msg>Introduced Event enum. Event will be used to return a Result whenever a chat even occurs in the channel<commit_after>
use message::Message;

pub struct TwitchClient {
	pub user: String,
	pub auth: String,
	pub default_channel: String,
	pub logging: bool,
}

pub enum Event {
	MessageReceived(Message),
}

impl TwitchClient {
	pub fn connect(&self) {
		// TODO: Connect!
	}

	pub fn recv_event(&self) -> Result<Event, &str> {

		Err("Not yet implemented")
	}
}
<|endoftext|>


<commit_before>
use std::os::raw::c_char;

#[link( name = "ncurses" )]
extern {
    pub fn initscr();
    pub fn refresh();
    pub fn endwin();
    pub fn getch() -> i32;
    pub fn printw( format : *const c_char, ... );
    pub fn cbreak();
    pub fn noecho();
    pub fn keypad();
    pub fn clear();
    pub fn mvprintw( y : i32, x : i32, format : *const c_char, ... );
}

extern {
    pub static error : i32;
    fn move_wrapper( y : i32, x : i32 );
}

pub unsafe fn cursor_move( y : i32, x : i32 ) {
    move_wrapper( y, x );
}

<commit_msg>adding mvaddch method from ncurses<commit_after>
use std::os::raw::c_char;

#[link( name = "ncurses" )]
extern {
    pub fn initscr();
    pub fn refresh();
    pub fn endwin();
    pub fn getch() -> i32;
    pub fn printw( format : *const c_char, ... );
    pub fn cbreak();
    pub fn noecho();
    pub fn keypad();
    pub fn clear();
    pub fn mvprintw( y : i32, x : i32, format : *const c_char, ... );
    pub fn mvaddch( y : i32, x : i32, c : char );
}

extern {
    pub static error : i32;
    fn move_wrapper( y : i32, x : i32 );
}

pub unsafe fn cursor_move( y : i32, x : i32 ) {
    move_wrapper( y, x );
}

<|endoftext|>


<commit_before>//! This module contains the Mastodon API Methods `/api/v1`.
//!

use api::oauth::OAuthApp;
use super::entities;

/// updatable fields for the authenticated user.
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct UserFormData {
    display_name: Option<String>,
    note: Option<String>,
    avatar: Option<String>,
    header: Option<String>
}

impl UserFormData {
    pub fn new(&self, display_name: Option<String>, note: Option<String>, avatar: Option<String>, header: Option<String>) -> Self {
        UserFormData {
            display_name,
            note,
            avatar,
            header
        }
    }
}

/// Methods for interacting with accounts on a Mastodon node.
pub trait Accounts {
    /// Fetching an account:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id
    /// ```
    ///
    /// Returns an `Account`.
    fn fetch_account(&self, account_id: usize) -> Result<entities::Account, &str>;

    /// Getting the current user:
    ///
    /// ```
    /// GET /api/v1/accounts/verify_credentials
    /// ```
    ///
    /// Returns the authenticated user's `Account`.
    fn get_current_user(&self) -> Result<entities::Account, &str>;

    /// Updating the current user:
    ///
    /// ```
    /// PATCH /api/v1/accounts/update_credentials
    /// ```
    ///
    /// `UserFormData` form data is required.
    fn update_current_user(&self, form_data: UserFormData) -> Result<entities::Account, &str>;

    /// Getting an account's followers:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/followers
    /// ```
    /// Query parameters:
    ///
    /// `max_id`   | Get a list of followers with ID less than or equal this value. It's optional.
    ///
    /// `since_id` | Get a list of followers with ID greater than this value. It's optional.
    ///
    /// `limit`    | Maximum number of followers to get (Default 40, Max 80). It's optional.
    ///
    /// Returns an array of `Account`s.
    fn get_account_followers(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;

    /// Get who account is following:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/following
    /// ```
    ///
    /// Query parameters:
    ///
    /// `max_id`   | Get a list of followers with ID less than or equal this value. It's optional.
    ///
    /// `since_id` | Get a list of followers with ID greater than this value. It's optional.
    ///
    /// `limit`    | Maximum number of followers to get (Default 40, Max 80). It's optional.
    ///
    /// Returns an array of `Account`s.
    fn get_account_following(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;

    /// Get an account's statuses:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/statuses
    /// ```
    ///
    /// Query parameters:
    ///
    /// `only_media`      | Only return statuses that have media attachments. It's optional.
    ///
    /// `exclude_replies` | Skip statuses that reply to other statuses. It's optional.
    ///
    /// `max_id`          | Get a list of statutes with ID less than or equal this value. It's optional.
    ///
    /// `since_id`        | Get a list of statutes with ID greater than this value. It's optional.
    ///
    /// `limit`           | Maximum number of statutes to get (Default 40, Max 80). It's optional.
    ///
    /// Returns an array of `Status`es.
    fn get_account_statutes(&self, account_id: usize) -> Result<Vec<entities::Status>, &str>;

    fn follow_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    fn unfollow_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    fn mute_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    fn unmute_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    fn get_account_relationships(&self, account_id: usize) -> Result<Vec<entities::Relationship>, &str>;

    fn search_accounts(&self, search_query: String) -> Result<Vec<entities::Account>, &str>;
}

pub trait Apps {
    fn register_app(&self, name: &str, uris: &str, scopes: &str) -> OAuthApp;
}

pub trait Blocks {
    fn block_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
    fn unblock_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
}
pub trait Favourites {
    fn favourite_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
    fn unfavourite_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
}

pub trait FollowRequests {
}

pub trait Follows {
}

pub trait Instances {
}

pub trait Media {
}

pub trait Mutes {
}

pub trait Notifications {
}

pub trait Reports {
}

pub trait Search {
}

pub trait Statuses {
}

pub trait Timelines {
}
<commit_msg>document Accounts trait for API Methods<commit_after>//! This module contains the Mastodon API Methods `/api/v1`.
//!

use api::oauth::OAuthApp;
use super::entities;

/// updatable fields for the authenticated user.
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct UserFormData {
    display_name: Option<String>,
    note: Option<String>,
    avatar: Option<String>,
    header: Option<String>
}

impl UserFormData {
    pub fn new(&self, display_name: Option<String>, note: Option<String>, avatar: Option<String>, header: Option<String>) -> Self {
        UserFormData {
            display_name,
            note,
            avatar,
            header
        }
    }
}

/// Methods for interacting with accounts on a Mastodon node.
pub trait Accounts {
    /// Fetching an account:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id
    /// ```
    ///
    /// Returns an `Account`.
    fn fetch_account(&self, account_id: usize) -> Result<entities::Account, &str>;

    /// Getting the current user:
    ///
    /// ```
    /// GET /api/v1/accounts/verify_credentials
    /// ```
    ///
    /// Returns the authenticated user's `Account`.
    fn get_current_user(&self) -> Result<entities::Account, &str>;

    /// Updating the current user:
    ///
    /// ```
    /// PATCH /api/v1/accounts/update_credentials
    /// ```
    ///
    /// `UserFormData` form data is required.
    fn update_current_user(&self, form_data: UserFormData) -> Result<entities::Account, &str>;

    /// Getting an account's followers:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/followers
    /// ```
    /// Query parameters:
    ///
    /// `max_id` Get a list of followers with ID less than or equal this value. It is optional.
    ///
    /// `since_id` Get a list of followers with ID greater than this value. It is optional.
    ///
    /// `limit` Maximum number of followers to get (Default 40, Max 80). It is optional.
    ///
    /// Returns an array of `Account`s.
    fn get_account_followers(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;

    /// Get who account is following:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/following
    /// ```
    ///
    /// Query parameters:
    ///
    /// `max_id` Get a list of followers with ID less than or equal this value. It is optional.
    ///
    /// `since_id` Get a list of followers with ID greater than this value. It is optional.
    ///
    /// `limit` Maximum number of followers to get (Default 40, Max 80). It is optional.
    ///
    /// Returns an array of `Account`s.
    fn get_account_following(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;

    /// Get an account's statuses:
    ///
    /// ```
    /// GET /api/v1/accounts/:account_id/statuses
    /// ```
    ///
    /// Query parameters:
    ///
    /// `only_media` Only return statuses that have media attachments. It is optional.
    ///
    /// `exclude_replies` Skip statuses that reply to other statuses. It is optional.
    ///
    /// `max_id` Get a list of statutes with ID less than or equal this value. It is optional.
    ///
    /// `since_id` Get a list of statutes with ID greater than this value. It is optional.
    ///
    /// `limit` Maximum number of statutes to get (Default 40, Max 80). It is optional.
    ///
    /// Returns an array of `Status`es.
    fn get_account_statutes(&self, account_id: usize) -> Result<Vec<entities::Status>, &str>;

    /// Following an account:
    ///
    /// ```
    /// POST /api/v1/accounts/:account_id/follow
    /// ```
    ///
    /// Returns the target account's `Relationship`.
    fn follow_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    /// Unfollowing an account:
    ///
    /// ```
    /// POST /api/v1/accounts/:account_id/unfollow
    /// ```
    ///
    /// Returns the target account's `Relationship`.
    fn unfollow_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    /// Muting an account:
    ///
    /// ```
    /// POST /api/v1/accounts/:account_id/mute
    /// ```
    ///
    /// Returns the target account's `Relationship`.
    fn mute_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    /// Unmuting an account:
    ///
    /// ```
    /// POST /api/v1/accounts/:account_id/unmute
    /// ```
    ///
    /// Returns the target account's `Relationship`.
    fn unmute_account(&self, account_id: usize) -> Result<entities::Relationship, &str>;

    /// Getting an account's relationships:
    ///
    /// ```
    /// GET /api/v1/accounts/relationships
    /// ```
    ///
    /// Query parameters:
    ///
    /// `id` Account IDs (can be an array). It is required.
    ///
    /// Returns an array of `Relationship`s of the current user to a list of given accounts.
    fn get_account_relationships(&self, account_id: usize) -> Result<Vec<entities::Relationship>, &str>;

    /// Searching for accounts:
    ///
    /// ```
    /// GET /api/v1/accounts/search
    /// ```
    ///
    /// Query parameters:
    ///
    /// `q` What to search for. It is required.
    ///
    /// `limit` Maximum number of matching accounts to return (default: `40`). It is optional.
    ///
    /// Returns an array of matching `Account`s.
    ///
    /// Will lookup an account remotely if the search term is in the `username@domain` format and
    /// not yet in the database.
    fn search_accounts(&self, search_query: String) -> Result<Vec<entities::Account>, &str>;
}

pub trait Apps {
    fn register_app(&self, name: &str, uris: &str, scopes: &str) -> OAuthApp;
}

pub trait Blocks {
    fn block_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
    fn unblock_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
}
pub trait Favourites {
    fn favourite_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
    fn unfavourite_account(&self, account_id: usize) -> Result<Vec<entities::Account>, &str>;
}

pub trait FollowRequests {
}

pub trait Follows {
}

pub trait Instances {
}

pub trait Media {
}

pub trait Mutes {
}

pub trait Notifications {
}

pub trait Reports {
}

pub trait Search {
}

pub trait Statuses {
}

pub trait Timelines {
}
<|endoftext|>


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

//! Runtime calls emitted by the compiler.

use uint;
use cast::transmute;
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
use managed::raw::BoxRepr;
use str;
use sys;
use unstable::exchange_alloc;
use cast::transmute;
use task::rt::rust_get_task;
use option::{Option, Some, None};

#[allow(non_camel_case_types)]
pub type rust_task = c_void;

pub static FROZEN_BIT: uint = 1 << (uint::bits - 1);
pub static MUT_BIT: uint = 1 << (uint::bits - 2);
static ALL_BITS: uint = FROZEN_BIT | MUT_BIT;

pub mod rustrt {
    use unstable::lang::rust_task;
    use libc::{c_void, c_char, uintptr_t};

    pub extern {
        #[rust_stack]
        unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char;

        #[rust_stack]
        unsafe fn rust_upcall_free(ptr: *c_char);

        #[fast_ffi]
        unsafe fn rust_upcall_malloc_noswitch(td: *c_char,
                                              size: uintptr_t)
                                           -> *c_char;

        #[fast_ffi]
        unsafe fn rust_upcall_free_noswitch(ptr: *c_char);

        #[rust_stack]
        fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;

        #[rust_stack]
        fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);

        fn rust_dbg_breakpoint();
    }
}

#[lang="fail_"]
pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
    sys::begin_unwind_(expr, file, line);
}

#[lang="fail_bounds_check"]
pub fn fail_bounds_check(file: *c_char, line: size_t,
                         index: size_t, len: size_t) {
    let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                    len as int, index as int);
    do str::as_buf(msg) |p, _len| {
        fail_(p as *c_char, file, line);
    }
}

#[deriving(Eq)]
struct BorrowRecord {
    box: *mut BoxRepr,
    file: *c_char,
    line: size_t
}

fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> {
    unsafe {
        let cur_task = rust_get_task();
        let ptr = rustrt::rust_take_task_borrow_list(cur_task);
        if ptr.is_null() {
            None
        } else {
            let v: ~[BorrowRecord] = transmute(ptr);
            Some(v)
        }
    }
}

fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
    unsafe {
        let cur_task = rust_get_task();
        let mut borrow_list: ~[BorrowRecord] = {
            let ptr = rustrt::rust_take_task_borrow_list(cur_task);
            if ptr.is_null() { ~[] } else { transmute(ptr) }
        };
        borrow_list = f(borrow_list);
        rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list));
    }
}

pub unsafe fn clear_task_borrow_list() {
    // pub because it is used by the box annihilator.
    let _ = try_take_task_borrow_list();
}

fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
    debug_ptr("fail_borrowed: ", box);

    match try_take_task_borrow_list() {
        None => { // not recording borrows
            let msg = "borrowed";
            do str::as_buf(msg) |msg_p, _| {
                fail_(msg_p as *c_char, file, line);
            }
        }
        Some(borrow_list) => { // recording borrows
            let mut msg = ~"borrowed";
            let mut sep = " at ";
            for borrow_list.each_reverse |entry| {
                if entry.box == box {
                    str::push_str(&mut msg, sep);
                    let filename = unsafe {
                        str::raw::from_c_str(entry.file)
                    };
                    str::push_str(&mut msg, filename);
                    str::push_str(&mut msg, fmt!(":%u", entry.line as uint));
                    sep = " and at ";
                }
            }
            do str::as_buf(msg) |msg_p, _| {
                fail_(msg_p as *c_char, file, line)
            }
        }
    }
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[lang="exchange_malloc"]
#[inline(always)]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size)));
    debug_ptr("exchange_malloc: ", result);
    return result;
}

/// Because this code is so perf. sensitive, use a static constant so that
/// debug printouts are compiled out most of the time.
static ENABLE_DEBUG_PTR: bool = true;

#[inline]
pub fn debug_ptr<T>(tag: &'static str, p: *const T) {
    //! A useful debugging function that prints a pointer + tag + newline
    //! without allocating memory.

    if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem {
        debug_ptr_slow(tag, p);
    }

    fn debug_ptr_slow<T>(tag: &'static str, p: *const T) {
        use io;
        let dbg = STDERR_FILENO as io::fd_t;
        let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
                       '9', 'a', 'b', 'c', 'd', 'e', 'f'];
        dbg.write_str(tag);

        static uint_nibbles: uint = ::uint::bytes << 1;
        let mut buffer = [0_u8, ..uint_nibbles+1];
        let mut i = p as uint;
        let mut c = uint_nibbles;
        while c > 0 {
            c -= 1;
            buffer[c] = letters[i & 0xF] as u8;
            i >>= 4;
        }
        dbg.write(buffer.slice(0, uint_nibbles));

        dbg.write_str("\n");
    }
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="exchange_free"]
#[inline(always)]
pub unsafe fn exchange_free(ptr: *c_char) {
    debug_ptr("exchange_free: ", ptr);
    exchange_alloc::free(transmute(ptr))
}

#[lang="malloc"]
#[inline(always)]
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let result = rustrt::rust_upcall_malloc_noswitch(td, size);
    debug_ptr("local_malloc: ", result);
    return result;
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[inline(always)]
pub unsafe fn local_free(ptr: *c_char) {
    debug_ptr("local_free: ", ptr);
    rustrt::rust_upcall_free_noswitch(ptr);
}

#[cfg(stage0)]
#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    (*a).header.ref_count |= FROZEN_BIT;
}

#[cfg(not(stage0))]
#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint {
    let a: *mut BoxRepr = transmute(a);
    let ref_count = (*a).header.ref_count;

    debug_ptr("borrow_as_imm (ptr) :", a);
    debug_ptr("              (ref) :", ref_count as *());
    debug_ptr("              (line): ", line as *());

    if (ref_count & MUT_BIT) != 0 {
        fail_borrowed(a, file, line);
    }

    ref_count
}

#[cfg(not(stage0))]
#[lang="borrow_as_mut"]
#[inline(always)]
pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint {
    let a: *mut BoxRepr = transmute(a);

    debug_ptr("borrow_as_mut (ptr): ", a);
    debug_ptr("              (line): ", line as *());

    let ref_count = (*a).header.ref_count;
    if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 {
        fail_borrowed(a, file, line);
    }
    ref_count
}

#[cfg(not(stage0))]
#[lang="record_borrow"]
pub unsafe fn record_borrow(a: *u8, old_ref_count: uint,
                            file: *c_char, line: size_t) {
    if (old_ref_count & ALL_BITS) == 0 {
        // was not borrowed before
        let a: *mut BoxRepr = transmute(a);
        do swap_task_borrow_list |borrow_list| {
            let mut borrow_list = borrow_list;
            borrow_list.push(BorrowRecord {box: a, file: file, line: line});
            borrow_list
        }
    }
}

#[cfg(not(stage0))]
#[lang="unrecord_borrow"]
pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
                              file: *c_char, line: size_t) {
    if (old_ref_count & ALL_BITS) == 0 {
        // was not borrowed before
        let a: *mut BoxRepr = transmute(a);
        do swap_task_borrow_list |borrow_list| {
            let mut borrow_list = borrow_list;
            let br = BorrowRecord {box: a, file: file, line: line};
            match borrow_list.rposition_elem(&br) {
                Some(idx) => {
                    borrow_list.remove(idx);
                    borrow_list
                }
                None => {
                    let err = fmt!("no borrow found, br=%?, borrow_list=%?",
                                   br, borrow_list);
                    do str::as_buf(err) |msg_p, _| {
                        fail_(msg_p as *c_char, file, line)
                    }
                }
            }
        }
    }
}

#[cfg(stage0)]
#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        (*a).header.ref_count &= !FROZEN_BIT;
    }
}

#[cfg(not(stage0))]
#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint,
                            file: *c_char, line: size_t) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        let ref_count = (*a).header.ref_count;
        let combined = (ref_count & !ALL_BITS) | (old_ref_count & ALL_BITS);
        (*a).header.ref_count = combined;

        debug_ptr("return_to_mut (ptr) : ", a);
        debug_ptr("              (line): ", line as *());
        debug_ptr("              (old) : ", old_ref_count as *());
        debug_ptr("              (new) : ", ref_count as *());
        debug_ptr("              (comb): ", combined as *());
    }
}

#[cfg(stage0)]
#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    if ((*a).header.ref_count & FROZEN_BIT) != 0 {
        do str::as_buf("XXX") |file_p, _| {
            fail_borrowed(a, file_p as *c_char, 0);
        }
    }
}

#[cfg(not(stage0))]
#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8,
                                 file: *c_char,
                                 line: size_t) {
    let a: *mut BoxRepr = transmute(a);
    if ((*a).header.ref_count & FROZEN_BIT) != 0 {
        fail_borrowed(a, file, line);
    }
}

#[lang="strdup_uniq"]
#[inline(always)]
pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
    str::raw::from_buf_len(ptr, len)
}

#[lang="start"]
pub fn start(main: *u8, argc: int, argv: **c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: **c_char,
                      crate_map: *c_void) -> c_int;
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>lang: um, actually set locking bits! this code got lost.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Runtime calls emitted by the compiler.

use uint;
use cast::transmute;
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
use managed::raw::BoxRepr;
use str;
use sys;
use unstable::exchange_alloc;
use cast::transmute;
use task::rt::rust_get_task;
use option::{Option, Some, None};

#[allow(non_camel_case_types)]
pub type rust_task = c_void;

pub static FROZEN_BIT: uint = 1 << (uint::bits - 1);
pub static MUT_BIT: uint = 1 << (uint::bits - 2);
static ALL_BITS: uint = FROZEN_BIT | MUT_BIT;

pub mod rustrt {
    use unstable::lang::rust_task;
    use libc::{c_void, c_char, uintptr_t};

    pub extern {
        #[rust_stack]
        unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char;

        #[rust_stack]
        unsafe fn rust_upcall_free(ptr: *c_char);

        #[fast_ffi]
        unsafe fn rust_upcall_malloc_noswitch(td: *c_char,
                                              size: uintptr_t)
                                           -> *c_char;

        #[fast_ffi]
        unsafe fn rust_upcall_free_noswitch(ptr: *c_char);

        #[rust_stack]
        fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;

        #[rust_stack]
        fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);

        fn rust_dbg_breakpoint();
    }
}

#[lang="fail_"]
pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
    sys::begin_unwind_(expr, file, line);
}

#[lang="fail_bounds_check"]
pub fn fail_bounds_check(file: *c_char, line: size_t,
                         index: size_t, len: size_t) {
    let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                    len as int, index as int);
    do str::as_buf(msg) |p, _len| {
        fail_(p as *c_char, file, line);
    }
}

#[deriving(Eq)]
struct BorrowRecord {
    box: *mut BoxRepr,
    file: *c_char,
    line: size_t
}

fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> {
    unsafe {
        let cur_task = rust_get_task();
        let ptr = rustrt::rust_take_task_borrow_list(cur_task);
        if ptr.is_null() {
            None
        } else {
            let v: ~[BorrowRecord] = transmute(ptr);
            Some(v)
        }
    }
}

fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
    unsafe {
        let cur_task = rust_get_task();
        let mut borrow_list: ~[BorrowRecord] = {
            let ptr = rustrt::rust_take_task_borrow_list(cur_task);
            if ptr.is_null() { ~[] } else { transmute(ptr) }
        };
        borrow_list = f(borrow_list);
        rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list));
    }
}

pub unsafe fn clear_task_borrow_list() {
    // pub because it is used by the box annihilator.
    let _ = try_take_task_borrow_list();
}

fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
    debug_ptr("fail_borrowed: ", box);

    match try_take_task_borrow_list() {
        None => { // not recording borrows
            let msg = "borrowed";
            do str::as_buf(msg) |msg_p, _| {
                fail_(msg_p as *c_char, file, line);
            }
        }
        Some(borrow_list) => { // recording borrows
            let mut msg = ~"borrowed";
            let mut sep = " at ";
            for borrow_list.each_reverse |entry| {
                if entry.box == box {
                    str::push_str(&mut msg, sep);
                    let filename = unsafe {
                        str::raw::from_c_str(entry.file)
                    };
                    str::push_str(&mut msg, filename);
                    str::push_str(&mut msg, fmt!(":%u", entry.line as uint));
                    sep = " and at ";
                }
            }
            do str::as_buf(msg) |msg_p, _| {
                fail_(msg_p as *c_char, file, line)
            }
        }
    }
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[lang="exchange_malloc"]
#[inline(always)]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size)));
    debug_ptr("exchange_malloc: ", result);
    return result;
}

/// Because this code is so perf. sensitive, use a static constant so that
/// debug printouts are compiled out most of the time.
static ENABLE_DEBUG_PTR: bool = true;

#[inline]
pub fn debug_ptr<T>(tag: &'static str, p: *const T) {
    //! A useful debugging function that prints a pointer + tag + newline
    //! without allocating memory.

    if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem {
        debug_ptr_slow(tag, p);
    }

    fn debug_ptr_slow<T>(tag: &'static str, p: *const T) {
        use io;
        let dbg = STDERR_FILENO as io::fd_t;
        let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
                       '9', 'a', 'b', 'c', 'd', 'e', 'f'];
        dbg.write_str(tag);

        static uint_nibbles: uint = ::uint::bytes << 1;
        let mut buffer = [0_u8, ..uint_nibbles+1];
        let mut i = p as uint;
        let mut c = uint_nibbles;
        while c > 0 {
            c -= 1;
            buffer[c] = letters[i & 0xF] as u8;
            i >>= 4;
        }
        dbg.write(buffer.slice(0, uint_nibbles));

        dbg.write_str("\n");
    }
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="exchange_free"]
#[inline(always)]
pub unsafe fn exchange_free(ptr: *c_char) {
    debug_ptr("exchange_free: ", ptr);
    exchange_alloc::free(transmute(ptr))
}

#[lang="malloc"]
#[inline(always)]
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let result = rustrt::rust_upcall_malloc_noswitch(td, size);
    debug_ptr("local_malloc: ", result);
    return result;
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[inline(always)]
pub unsafe fn local_free(ptr: *c_char) {
    debug_ptr("local_free: ", ptr);
    rustrt::rust_upcall_free_noswitch(ptr);
}

#[cfg(stage0)]
#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    (*a).header.ref_count |= FROZEN_BIT;
}

#[cfg(not(stage0))]
#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint {
    let a: *mut BoxRepr = transmute(a);
    let ref_count = (*a).header.ref_count;

    debug_ptr("borrow_as_imm (ptr) :", a);
    debug_ptr("              (ref) :", ref_count as *());
    debug_ptr("              (line): ", line as *());

    if (ref_count & MUT_BIT) != 0 {
        fail_borrowed(a, file, line);
    }

    (*a).header.ref_count = ref_count | FROZEN_BIT;

    ref_count
}

#[cfg(not(stage0))]
#[lang="borrow_as_mut"]
#[inline(always)]
pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint {
    let a: *mut BoxRepr = transmute(a);

    debug_ptr("borrow_as_mut (ptr): ", a);
    debug_ptr("              (line): ", line as *());

    let ref_count = (*a).header.ref_count;
    if (ref_count & (MUT_BIT|FROZEN_BIT)) != 0 {
        fail_borrowed(a, file, line);
    }

    (*a).header.ref_count = ref_count | MUT_BIT | FROZEN_BIT;

    ref_count
}

#[cfg(not(stage0))]
#[lang="record_borrow"]
pub unsafe fn record_borrow(a: *u8, old_ref_count: uint,
                            file: *c_char, line: size_t) {
    if (old_ref_count & ALL_BITS) == 0 {
        // was not borrowed before
        let a: *mut BoxRepr = transmute(a);
        do swap_task_borrow_list |borrow_list| {
            let mut borrow_list = borrow_list;
            borrow_list.push(BorrowRecord {box: a, file: file, line: line});
            borrow_list
        }
    }
}

#[cfg(not(stage0))]
#[lang="unrecord_borrow"]
pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
                              file: *c_char, line: size_t) {
    if (old_ref_count & ALL_BITS) == 0 {
        // was not borrowed before
        let a: *mut BoxRepr = transmute(a);
        do swap_task_borrow_list |borrow_list| {
            let mut borrow_list = borrow_list;
            let br = BorrowRecord {box: a, file: file, line: line};
            match borrow_list.rposition_elem(&br) {
                Some(idx) => {
                    borrow_list.remove(idx);
                    borrow_list
                }
                None => {
                    let err = fmt!("no borrow found, br=%?, borrow_list=%?",
                                   br, borrow_list);
                    do str::as_buf(err) |msg_p, _| {
                        fail_(msg_p as *c_char, file, line)
                    }
                }
            }
        }
    }
}

#[cfg(stage0)]
#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        (*a).header.ref_count &= !FROZEN_BIT;
    }
}

#[cfg(not(stage0))]
#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8, old_ref_count: uint,
                            file: *c_char, line: size_t) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        let ref_count = (*a).header.ref_count;
        let combined = (ref_count & !ALL_BITS) | (old_ref_count & ALL_BITS);
        (*a).header.ref_count = combined;

        debug_ptr("return_to_mut (ptr) : ", a);
        debug_ptr("              (line): ", line as *());
        debug_ptr("              (old) : ", old_ref_count as *());
        debug_ptr("              (new) : ", ref_count as *());
        debug_ptr("              (comb): ", combined as *());
    }
}

#[cfg(stage0)]
#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    if ((*a).header.ref_count & FROZEN_BIT) != 0 {
        do str::as_buf("XXX") |file_p, _| {
            fail_borrowed(a, file_p as *c_char, 0);
        }
    }
}

#[cfg(not(stage0))]
#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8,
                                 file: *c_char,
                                 line: size_t) {
    let a: *mut BoxRepr = transmute(a);
    let ref_count = (*a).header.ref_count;
    debug_ptr("check_not_borrowed (ptr) : ", a);
    debug_ptr("                   (line): ", line as *());
    debug_ptr("                   (rc)  : ", ref_count as *());

    if (ref_count & FROZEN_BIT) != 0 {
        fail_borrowed(a, file, line);
    }
}

#[lang="strdup_uniq"]
#[inline(always)]
pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
    str::raw::from_buf_len(ptr, len)
}

#[lang="start"]
pub fn start(main: *u8, argc: int, argv: **c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: **c_char,
                      crate_map: *c_void) -> c_int;
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align", "width"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let mut options = comrak::ComrakOptions::default();
        options.ext_autolink = true;
        options.ext_strikethrough = true;
        options.ext_table = true;
        options.ext_tagfilter = true;
        options.ext_tasklist = true;

        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("script"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("iframe"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_unknwon_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("unknown"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("<a"), true);
        assert_eq!(rendered.contains("href="), true);
        assert_eq!(rendered.contains("onclick"), false);
        assert_eq!(rendered.contains("window.alert"), false);
        assert_eq!(rendered.contains("Crate page"), true);
    }
}
<commit_msg>Use cleaner syntax for ComrakOptions<commit_after>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align", "width"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("script"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("iframe"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_unknwon_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("unknown"), false);
        assert_eq!(rendered.contains("alert('Hello World')"), true);
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text);
        assert_eq!(result.is_ok(), true);
        let rendered = result.unwrap();
        assert_eq!(rendered.contains("foo_readme"), true);
        assert_eq!(rendered.contains("<a"), true);
        assert_eq!(rendered.contains("href="), true);
        assert_eq!(rendered.contains("onclick"), false);
        assert_eq!(rendered.contains("window.alert"), false);
        assert_eq!(rendered.contains("Crate page"), true);
    }
}
<|endoftext|>


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

//! An efficient hash map for node IDs

use collections::{HashMap, HashSet};
use std::hash::{Hasher, Hash};
use std::io;
use syntax::ast;

#[cfg(not(stage0))]
pub type NodeMap<T> = HashMap<ast::NodeId, T, FnvHasher>;
#[cfg(not(stage0))]
pub type DefIdMap<T> = HashMap<ast::DefId, T, FnvHasher>;
#[cfg(not(stage0))]
pub type NodeSet = HashSet<ast::NodeId, FnvHasher>;
#[cfg(not(stage0))]
pub type DefIdSet = HashSet<ast::DefId, FnvHasher>;

// Hacks to get good names
#[cfg(not(stage0))]
pub mod NodeMap {
    use collections::HashMap;
    pub fn new<T>() -> super::NodeMap<T> {
        HashMap::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod NodeSet {
    use collections::HashSet;
    pub fn new() -> super::NodeSet {
        HashSet::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod DefIdMap {
    use collections::HashMap;
    pub fn new<T>() -> super::DefIdMap<T> {
        HashMap::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod DefIdSet {
    use collections::HashSet;
    pub fn new() -> super::DefIdSet {
        HashSet::with_hasher(super::FnvHasher)
    }
}

#[cfg(stage0)]
pub type NodeMap<T> = HashMap<ast::NodeId, T>;
#[cfg(stage0)]
pub type DefIdMap<T> = HashMap<ast::DefId, T>;
#[cfg(stage0)]
pub type NodeSet = HashSet<ast::NodeId>;
#[cfg(stage0)]
pub type DefIdSet = HashSet<ast::DefId>;

// Hacks to get good names
#[cfg(stage0)]
pub mod NodeMap {
    use collections::HashMap;
    pub fn new<T>() -> super::NodeMap<T> {
        HashMap::new()
    }
}
#[cfg(stage0)]
pub mod NodeSet {
    use collections::HashSet;
    pub fn new() -> super::NodeSet {
        HashSet::new()
    }
}
#[cfg(stage0)]
pub mod DefIdMap {
    use collections::HashMap;
    pub fn new<T>() -> super::DefIdMap<T> {
        HashMap::new()
    }
}
#[cfg(stage0)]
pub mod DefIdSet {
    use collections::HashSet;
    pub fn new() -> super::DefIdSet {
        HashSet::new()
    }
}

/// A speedy hash algorithm for node ids and def ids. The hashmap in
/// libcollections by default uses SipHash which isn't quite as speedy as we
/// want. In the compiler we're not really worried about DOS attempts, so we
/// just default to a non-cryptographic hash.
///
/// This uses FNV hashing, as described here:
/// http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
#[deriving(Clone)]
pub struct FnvHasher;

pub struct FnvState(u64);

impl Hasher<FnvState> for FnvHasher {
    fn hash<T: Hash<FnvState>>(&self, t: &T) -> u64 {
        let mut state = FnvState(0xcbf29ce484222325);
        t.hash(&mut state);
        let FnvState(ret) = state;
        return ret;
    }
}

impl Writer for FnvState {
    fn write(&mut self, bytes: &[u8]) -> io::IoResult<()> {
        let FnvState(mut hash) = *self;
        for byte in bytes.iter() {
            hash = hash * 0x100000001b3;
            hash = hash ^ (*byte as u64);
        }
        *self = FnvState(hash);
        Ok(())
    }
}
<commit_msg>auto merge of #12749 : tedhorst/rust/master, r=huonw<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! An efficient hash map for node IDs

use collections::{HashMap, HashSet};
use std::hash::{Hasher, Hash};
use std::io;
use syntax::ast;

#[cfg(not(stage0))]
pub type NodeMap<T> = HashMap<ast::NodeId, T, FnvHasher>;
#[cfg(not(stage0))]
pub type DefIdMap<T> = HashMap<ast::DefId, T, FnvHasher>;
#[cfg(not(stage0))]
pub type NodeSet = HashSet<ast::NodeId, FnvHasher>;
#[cfg(not(stage0))]
pub type DefIdSet = HashSet<ast::DefId, FnvHasher>;

// Hacks to get good names
#[cfg(not(stage0))]
pub mod NodeMap {
    use collections::HashMap;
    pub fn new<T>() -> super::NodeMap<T> {
        HashMap::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod NodeSet {
    use collections::HashSet;
    pub fn new() -> super::NodeSet {
        HashSet::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod DefIdMap {
    use collections::HashMap;
    pub fn new<T>() -> super::DefIdMap<T> {
        HashMap::with_hasher(super::FnvHasher)
    }
}
#[cfg(not(stage0))]
pub mod DefIdSet {
    use collections::HashSet;
    pub fn new() -> super::DefIdSet {
        HashSet::with_hasher(super::FnvHasher)
    }
}

#[cfg(stage0)]
pub type NodeMap<T> = HashMap<ast::NodeId, T>;
#[cfg(stage0)]
pub type DefIdMap<T> = HashMap<ast::DefId, T>;
#[cfg(stage0)]
pub type NodeSet = HashSet<ast::NodeId>;
#[cfg(stage0)]
pub type DefIdSet = HashSet<ast::DefId>;

// Hacks to get good names
#[cfg(stage0)]
pub mod NodeMap {
    use collections::HashMap;
    pub fn new<T>() -> super::NodeMap<T> {
        HashMap::new()
    }
}
#[cfg(stage0)]
pub mod NodeSet {
    use collections::HashSet;
    pub fn new() -> super::NodeSet {
        HashSet::new()
    }
}
#[cfg(stage0)]
pub mod DefIdMap {
    use collections::HashMap;
    pub fn new<T>() -> super::DefIdMap<T> {
        HashMap::new()
    }
}
#[cfg(stage0)]
pub mod DefIdSet {
    use collections::HashSet;
    pub fn new() -> super::DefIdSet {
        HashSet::new()
    }
}

/// A speedy hash algorithm for node ids and def ids. The hashmap in
/// libcollections by default uses SipHash which isn't quite as speedy as we
/// want. In the compiler we're not really worried about DOS attempts, so we
/// just default to a non-cryptographic hash.
///
/// This uses FNV hashing, as described here:
/// http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
#[deriving(Clone)]
pub struct FnvHasher;

pub struct FnvState(u64);

impl Hasher<FnvState> for FnvHasher {
    fn hash<T: Hash<FnvState>>(&self, t: &T) -> u64 {
        let mut state = FnvState(0xcbf29ce484222325);
        t.hash(&mut state);
        let FnvState(ret) = state;
        return ret;
    }
}

impl Writer for FnvState {
    fn write(&mut self, bytes: &[u8]) -> io::IoResult<()> {
        let FnvState(mut hash) = *self;
        for byte in bytes.iter() {
            hash = hash ^ (*byte as u64);
            hash = hash * 0x100000001b3;
        }
        *self = FnvState(hash);
        Ok(())
    }
}
<|endoftext|>