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

extern mod extra;

use std::{io, run, os, str};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::io::fs::File;
use std::io::stdin;
use std::io::signal::{Listener, Interrupt};
use extra::getopts;

static exit_code : i32 = -999;
static nop_code : i32 = -1;

struct Shell {
    cmd_prompt : ~str,
    cmd_history : ~[~str],
}

impl Shell {

    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            cmd_history: ~[],
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        let (port_func, chan_func) : (Port<i32>, Chan<i32>) = Chan::new();
        let (port_status, chan_status) : (Port<i32>, Chan<i32>) = Chan::new();
        chan_status.send(nop_code);
        spawn(proc() {
            let mut listener = Listener::new();
            listener.register(Interrupt);
            loop {
                match port_func.try_recv() {
                    Some(status) if status == exit_code => {
                        listener.unregister(Interrupt);
                        unsafe { std::libc::exit(1 as std::libc::c_int); }
                        // return; // hangs if child processes running, so we used exit.
                    }
                    Some(response) => { chan_status.send(response); }   // To complete process
                    None    => {}
                }
            }
        });

        loop {
            port_status.recv();     // Wait for last op to finish.
            print(self.cmd_prompt); // Prints "gash >"
            io::stdio::flush();

            let line : ~str = stdin.read_line().unwrap().to_owned();
            let mut cmd_line: ~str = line.trim().to_owned();
            let mut background: bool = false;

            if cmd_line.char_len() > 0 {
                background = cmd_line.char_at(cmd_line.char_len() - 1) == '&';
                if background {
                    cmd_line = cmd_line.slice(0, cmd_line.char_len() - 1).trim().to_owned();
                }
            }

            let params = cmd_line.clone().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""          =>  { chan_func.send(nop_code);
                                  continue; }
                "exit"      =>  { chan_func.send(exit_code); }  // Exit
                "cd"        =>  { Shell::run_check_mode(background, Shell::run_cd(params), &chan_func); }
                "history"   =>  { Shell::run_check_mode(background, Shell::run_history(params, self.cmd_history), &chan_func); }
                _           =>  { Shell::run_check_mode(background, Shell::run_cmdline(params), &chan_func); }
            }

            self.cmd_history.push(program.to_owned());
        }
    }

    fn run_check_mode(background: bool, f: proc(), chan_func : &Chan<i32>) {
        let (port_temp, chan_temp) : (Port<i32>, Chan<i32>) = Chan::new();
        if background {
            spawn(proc() {
                f();
            });
            chan_func.send(nop_code);  // Return immediately.
        } else {
            spawn(proc() {
                f();
                chan_temp.send(nop_code); // Wait until f returns.
            });
            chan_func.send(port_temp.recv());   // Pass on the message.
        }
    }

    fn run_cmdline_single(cmd_line: &str) {
        (Shell::run_cmdline(cmd_line))();
    }

    fn run_cmdline(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            if params.trim() != "" {
                let output = Shell::handle_pipes(params, false);
                // write output to file (if necessary and exists)
                match output {
                    Some(outString) => {
                        match Shell::get_output_file(params) {
                            Some(fileName) => {
                                let mut f = File::create(&Path::new(fileName));
                                f.write(outString.into_bytes());
                            }
                            None => { /* Shouldn't happen... */ }
                        }
                    }
                    None => {}
                }
            }
        };
    }

    fn handle_pipes(cmd_line: &str, out_pipe: bool) -> Option<~str> {
        let pipeSplit = Shell::split_on_last_pipe(cmd_line);
        let mut argv = Shell::get_args_no_redirects(pipeSplit[0]);
        let program = argv.remove(0);
        let hasOutRedirect = match Shell::get_output_file(cmd_line) {
            Some(_) => { true }
            None => {
                if out_pipe { true }
                else { false }
            }
        };

        if pipeSplit.len() == 2 {
            return Shell::run_cmd(program, argv, Shell::handle_pipes(pipeSplit[1], true), hasOutRedirect);
        } else {
            return Shell::run_cmd(program, argv, Shell::get_input_file_contents(cmd_line), hasOutRedirect);
        }
    }

    fn run_cmd(program: &str, argv: &[~str], inputStr: Option<~str>, hasOutRedirect: bool) -> Option<~str> {
        if Shell::cmd_exists(program) {
            let mut options = run::ProcessOptions::new();
            options.in_fd = match inputStr {
                Some(_) => { None }
                None => { Some(0) } // 0 is stdin.
            };

            options.out_fd = if hasOutRedirect {
                None
            } else {
                Some(1) // 1 is stdout.
            };

            let mut process = run::Process::new(program, argv, options).unwrap();

            unsafe {
                println!("PID is {:d}", process.get_id());
                println!("Changing PID: {:d}", std::libc::setpgid(process.get_id(), process.get_id()));
                println!("PID is now {:d}", process.get_id());
                println!("Error is {:d} with string {:s}", os::errno(), os::last_os_error());
            }

            match inputStr {
                Some(string) => {
                    let buf = string.into_bytes();
                    process.input().write(buf);
                }
                None => {}
            }

            process.close_input();

            if hasOutRedirect {
                let processOutput = process.finish_with_output();
                return Some(str::from_utf8(processOutput.output).to_owned());
            } else {
                process.finish();
                return None;
            };
        } else {
            println!("{:s}: command not found", program);
            return None;
        }
    }

    fn cmd_exists(cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    fn run_cd(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            let pathOpt: Option<Path> = match argv.len() {
                1   =>  { os::homedir() }
                0   =>  { os::homedir() }
                _   =>  { Some(Path::new(argv[1])) }
            };

            match pathOpt {
                Some(path)   =>  {
                    if path.is_dir() {
                        os::change_dir(&path);
                    } else {
                        println!("Error: {:s} is not a directory", path.as_str().unwrap());
                    }
                }
                None        =>  {
                    println!("Error: Invalid path");
                }
            };
        };
    }

    fn run_history(cmd_line: &str, cmd_history: &[~str]) -> proc() {
        let history = cmd_history.to_owned();
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            if (argv.len() > 1) {
                println!("Error: history does not take options (sadly).");
            }
            else {
                let mut i = 1;
                for entry in history.iter() {
                    println!("{:d} \t{:s}", i, entry.to_owned());
                    i = i + 1;
                }
            }
        };
    }

    fn split_on_last_pipe(cmd_line: &str) -> ~[~str] {
        return cmd_line.rsplitn('|', 1).filter_map(|x| Some(x.trim().to_owned())).to_owned_vec();
    }

    fn get_input_file_contents(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('<', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => {
                        match File::open(&Path::new(inputFile)) {
                            Some(mut file) => {
                                Some(file.read_to_str())
                            }
                            None => {
                                println!("File {:s} does not exist", inputFile);
                                None
                            }
                        }
                    }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_output_file(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('>', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => { Some(inputFile.to_owned()) }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_args_no_redirects(cmd_line: &str) -> ~[~str] {
        let mut activeRedirect: bool = false;
        let mut args: ~[~str] = ~[];
        let mut input: ~[~str] = Shell::get_args(cmd_line);
        for arg in input.mut_iter() {
            if *arg == ~">" || *arg == ~"<" {
                activeRedirect = true;
            } else if activeRedirect {
                activeRedirect = false;
            } else {
                let newArg = arg.to_owned();
                args.push(newArg);
            }
        }

        let result = args;
        return result;
    }

    fn get_args(cmd_line: &str) -> ~[~str] {
        let mut current: ~str = ~"";
        let mut escape: bool = false;
        let mut quoted: bool = false;
        let mut params: ~[~str] = ~[];

        for symbol in cmd_line.chars() {
            if escape {
                if symbol == 't' || symbol == 'n' || symbol == 'r' {
                    current = current + "\\";
                }
                current = current + str::from_char(symbol);
                escape = false;
            } else if symbol == ' ' || symbol == '\t' {
                if quoted {
                    current = current + str::from_char(symbol);
                } else if current.char_len() != 0 {
                    params.push(current.to_owned());
                    current = ~"";
                }
            } else if symbol == '\\' {
                escape = true;
            } else if symbol == '"' {
                quoted = !quoted;
            } else {
                current = current + str::from_char(symbol);
            }
        }

        if current.char_len() > 0 {
            params.push(current.to_owned());
        }

        let result = params;
        return result;
    }
}

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

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

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

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

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

    match opt_cmd_line {
        Some(cmd_line) => Shell::run_cmdline_single(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>Removed some test code that wasn't working for PIDs.<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os, str};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::io::fs::File;
use std::io::stdin;
use std::io::signal::{Listener, Interrupt};
use extra::getopts;

static exit_code : i32 = -999;
static nop_code : i32 = -1;

struct Shell {
    cmd_prompt : ~str,
    cmd_history : ~[~str],
}

impl Shell {

    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            cmd_history: ~[],
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        let (port_func, chan_func) : (Port<i32>, Chan<i32>) = Chan::new();
        let (port_status, chan_status) : (Port<i32>, Chan<i32>) = Chan::new();
        chan_status.send(nop_code);
        spawn(proc() {
            let mut listener = Listener::new();
            listener.register(Interrupt);
            loop {
                match port_func.try_recv() {
                    Some(status) if status == exit_code => {
                        listener.unregister(Interrupt);
                        unsafe { std::libc::exit(1 as std::libc::c_int); }
                        // return; // hangs if child processes running, so we used exit.
                    }
                    Some(response) => { chan_status.send(response); }   // To complete process
                    None    => {}
                }
            }
        });

        loop {
            port_status.recv();     // Wait for last op to finish.
            print(self.cmd_prompt); // Prints "gash >"
            io::stdio::flush();

            let line : ~str = stdin.read_line().unwrap().to_owned();
            let mut cmd_line: ~str = line.trim().to_owned();
            let mut background: bool = false;

            if cmd_line.char_len() > 0 {
                background = cmd_line.char_at(cmd_line.char_len() - 1) == '&';
                if background {
                    cmd_line = cmd_line.slice(0, cmd_line.char_len() - 1).trim().to_owned();
                }
            }

            let params = cmd_line.clone().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""          =>  { chan_func.send(nop_code);
                                  continue; }
                "exit"      =>  { chan_func.send(exit_code); }  // Exit
                "cd"        =>  { Shell::run_check_mode(background, Shell::run_cd(params), &chan_func); }
                "history"   =>  { Shell::run_check_mode(background, Shell::run_history(params, self.cmd_history), &chan_func); }
                _           =>  { Shell::run_check_mode(background, Shell::run_cmdline(params), &chan_func); }
            }

            self.cmd_history.push(program.to_owned());
        }
    }

    fn run_check_mode(background: bool, f: proc(), chan_func : &Chan<i32>) {
        let (port_temp, chan_temp) : (Port<i32>, Chan<i32>) = Chan::new();
        if background {
            spawn(proc() {
                f();
            });
            chan_func.send(nop_code);  // Return immediately.
        } else {
            spawn(proc() {
                f();
                chan_temp.send(nop_code); // Wait until f returns.
            });
            chan_func.send(port_temp.recv());   // Pass on the message.
        }
    }

    fn run_cmdline_single(cmd_line: &str) {
        (Shell::run_cmdline(cmd_line))();
    }

    fn run_cmdline(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            if params.trim() != "" {
                let output = Shell::handle_pipes(params, false);
                // write output to file (if necessary and exists)
                match output {
                    Some(outString) => {
                        match Shell::get_output_file(params) {
                            Some(fileName) => {
                                let mut f = File::create(&Path::new(fileName));
                                f.write(outString.into_bytes());
                            }
                            None => { /* Shouldn't happen... */ }
                        }
                    }
                    None => {}
                }
            }
        };
    }

    fn handle_pipes(cmd_line: &str, out_pipe: bool) -> Option<~str> {
        let pipeSplit = Shell::split_on_last_pipe(cmd_line);
        let mut argv = Shell::get_args_no_redirects(pipeSplit[0]);
        let program = argv.remove(0);
        let hasOutRedirect = match Shell::get_output_file(cmd_line) {
            Some(_) => { true }
            None => {
                if out_pipe { true }
                else { false }
            }
        };

        if pipeSplit.len() == 2 {
            return Shell::run_cmd(program, argv, Shell::handle_pipes(pipeSplit[1], true), hasOutRedirect);
        } else {
            return Shell::run_cmd(program, argv, Shell::get_input_file_contents(cmd_line), hasOutRedirect);
        }
    }

    fn run_cmd(program: &str, argv: &[~str], inputStr: Option<~str>, hasOutRedirect: bool) -> Option<~str> {
        if Shell::cmd_exists(program) {
            let mut options = run::ProcessOptions::new();
            options.in_fd = match inputStr {
                Some(_) => { None }
                None => { Some(0) } // 0 is stdin.
            };

            options.out_fd = if hasOutRedirect {
                None
            } else {
                Some(1) // 1 is stdout.
            };

            let mut process = run::Process::new(program, argv, options).unwrap();

            match inputStr {
                Some(string) => {
                    let buf = string.into_bytes();
                    process.input().write(buf);
                }
                None => {}
            }

            process.close_input();

            if hasOutRedirect {
                let processOutput = process.finish_with_output();
                return Some(str::from_utf8(processOutput.output).to_owned());
            } else {
                process.finish();
                return None;
            };
        } else {
            println!("{:s}: command not found", program);
            return None;
        }
    }

    fn cmd_exists(cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    fn run_cd(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            let pathOpt: Option<Path> = match argv.len() {
                1   =>  { os::homedir() }
                0   =>  { os::homedir() }
                _   =>  { Some(Path::new(argv[1])) }
            };

            match pathOpt {
                Some(path)   =>  {
                    if path.is_dir() {
                        os::change_dir(&path);
                    } else {
                        println!("Error: {:s} is not a directory", path.as_str().unwrap());
                    }
                }
                None        =>  {
                    println!("Error: Invalid path");
                }
            };
        };
    }

    fn run_history(cmd_line: &str, cmd_history: &[~str]) -> proc() {
        let history = cmd_history.to_owned();
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            if (argv.len() > 1) {
                println!("Error: history does not take options (sadly).");
            }
            else {
                let mut i = 1;
                for entry in history.iter() {
                    println!("{:d} \t{:s}", i, entry.to_owned());
                    i = i + 1;
                }
            }
        };
    }

    fn split_on_last_pipe(cmd_line: &str) -> ~[~str] {
        return cmd_line.rsplitn('|', 1).filter_map(|x| Some(x.trim().to_owned())).to_owned_vec();
    }

    fn get_input_file_contents(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('<', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => {
                        match File::open(&Path::new(inputFile)) {
                            Some(mut file) => {
                                Some(file.read_to_str())
                            }
                            None => {
                                println!("File {:s} does not exist", inputFile);
                                None
                            }
                        }
                    }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_output_file(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('>', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => { Some(inputFile.to_owned()) }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_args_no_redirects(cmd_line: &str) -> ~[~str] {
        let mut activeRedirect: bool = false;
        let mut args: ~[~str] = ~[];
        let mut input: ~[~str] = Shell::get_args(cmd_line);
        for arg in input.mut_iter() {
            if *arg == ~">" || *arg == ~"<" {
                activeRedirect = true;
            } else if activeRedirect {
                activeRedirect = false;
            } else {
                let newArg = arg.to_owned();
                args.push(newArg);
            }
        }

        let result = args;
        return result;
    }

    fn get_args(cmd_line: &str) -> ~[~str] {
        let mut current: ~str = ~"";
        let mut escape: bool = false;
        let mut quoted: bool = false;
        let mut params: ~[~str] = ~[];

        for symbol in cmd_line.chars() {
            if escape {
                if symbol == 't' || symbol == 'n' || symbol == 'r' {
                    current = current + "\\";
                }
                current = current + str::from_char(symbol);
                escape = false;
            } else if symbol == ' ' || symbol == '\t' {
                if quoted {
                    current = current + str::from_char(symbol);
                } else if current.char_len() != 0 {
                    params.push(current.to_owned());
                    current = ~"";
                }
            } else if symbol == '\\' {
                escape = true;
            } else if symbol == '"' {
                quoted = !quoted;
            } else {
                current = current + str::from_char(symbol);
            }
        }

        if current.char_len() > 0 {
            params.push(current.to_owned());
        }

        let result = params;
        return result;
    }
}

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

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

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

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

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

    match opt_cmd_line {
        Some(cmd_line) => Shell::run_cmdline_single(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<|endoftext|>


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

use super::lang_mapping;
use crate::error::ParseError;
use crate::Lang;

/// Represents a writing system (Latin, Cyrillic, Arabic, etc).
#[cfg_attr(feature = "enum-map", derive(::enum_map::Enum))]
#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    // Keep this in alphabetic order (for C bindings)
    Arabic,
    Armenian,
    Bengali,
    Cyrillic,
    Devanagari,
    Ethiopic,
    Georgian,
    Greek,
    Gujarati,
    Gurmukhi,
    Hangul,
    Hebrew,
    Hiragana,
    Kannada,
    Katakana,
    Khmer,
    Latin,
    Malayalam,
    Mandarin,
    Myanmar,
    Oriya,
    Sinhala,
    Tamil,
    Telugu,
    Thai,
}

// Array of all existing Script values.
const VALUES: [Script; 25] = [
    Script::Arabic,
    Script::Armenian,
    Script::Bengali,
    Script::Cyrillic,
    Script::Devanagari,
    Script::Ethiopic,
    Script::Georgian,
    Script::Greek,
    Script::Gujarati,
    Script::Gurmukhi,
    Script::Hangul,
    Script::Hebrew,
    Script::Hiragana,
    Script::Kannada,
    Script::Katakana,
    Script::Khmer,
    Script::Latin,
    Script::Malayalam,
    Script::Mandarin,
    Script::Myanmar,
    Script::Oriya,
    Script::Sinhala,
    Script::Tamil,
    Script::Telugu,
    Script::Thai,
];

impl Script {
    /// Get all existing scripts.
    ///
    /// # Example
    /// ```
    /// use whatlang::Script;
    /// for script in Script::all() {
    ///     println!("{}", script);
    /// }
    /// ```
    pub fn all() -> &'static [Script] {
        &VALUES
    }

    pub fn name(&self) -> &str {
        match *self {
            Script::Latin => "Latin",
            Script::Cyrillic => "Cyrillic",
            Script::Arabic => "Arabic",
            Script::Devanagari => "Devanagari",
            Script::Hiragana => "Hiragana",
            Script::Katakana => "Katakana",
            Script::Ethiopic => "Ethiopic",
            Script::Hebrew => "Hebrew",
            Script::Bengali => "Bengali",
            Script::Georgian => "Georgian",
            Script::Mandarin => "Mandarin",
            Script::Hangul => "Hangul",
            Script::Greek => "Greek",
            Script::Kannada => "Kannada",
            Script::Tamil => "Tamil",
            Script::Thai => "Thai",
            Script::Gujarati => "Gujarati",
            Script::Gurmukhi => "Gurmukhi",
            Script::Telugu => "Telugu",
            Script::Malayalam => "Malayalam",
            Script::Oriya => "Oriya",
            Script::Myanmar => "Myanmar",
            Script::Sinhala => "Sinhala",
            Script::Khmer => "Khmer",
            Script::Armenian => "Armenian",
        }
    }

    pub fn langs(&self) -> &[Lang] {
        lang_mapping::script_langs(*self)
    }
}

impl fmt::Display for Script {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl FromStr for Script {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "latin" => Ok(Script::Latin),
            "cyrillic" => Ok(Script::Cyrillic),
            "arabic" => Ok(Script::Arabic),
            "devanagari" => Ok(Script::Devanagari),
            "hiragana" => Ok(Script::Hiragana),
            "katakana" => Ok(Script::Katakana),
            "ethiopic" => Ok(Script::Ethiopic),
            "hebrew" => Ok(Script::Hebrew),
            "bengali" => Ok(Script::Bengali),
            "georgian" => Ok(Script::Georgian),
            "mandarin" => Ok(Script::Mandarin),
            "hangul" => Ok(Script::Hangul),
            "greek" => Ok(Script::Greek),
            "kannada" => Ok(Script::Kannada),
            "tamil" => Ok(Script::Tamil),
            "thai" => Ok(Script::Thai),
            "gujarati" => Ok(Script::Gujarati),
            "gurmukhi" => Ok(Script::Gurmukhi),
            "telugu" => Ok(Script::Telugu),
            "malayalam" => Ok(Script::Malayalam),
            "oriya" => Ok(Script::Oriya),
            "myanmar" => Ok(Script::Myanmar),
            "sinhala" => Ok(Script::Sinhala),
            "khmer" => Ok(Script::Khmer),
            "armenian" => Ok(Script::Armenian),
            _ => Err(ParseError::Script(s.to_string())),
        }
    }
}

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

    #[test]
    fn test_all() {
        assert_eq!(Script::all().len(), 25);
        let all = Script::all();
        assert!(all.contains(&Script::Cyrillic));
        assert!(all.contains(&Script::Arabic));
        assert!(all.contains(&Script::Latin));
    }

    #[test]
    fn test_from_str() {
        for &script in Script::all() {
            let s = script.name();
            assert_eq!(s.parse::<Script>().unwrap(), script);
            assert_eq!(s.to_lowercase().parse::<Script>().unwrap(), script);
            assert_eq!(s.to_uppercase().parse::<Script>().unwrap(), script);
        }

        let result = "foobar".parse::<Script>();
        assert!(matches!(result, Err(ParseError::Script(_))));
    }

    #[test]
    fn test_langs() {
        // Vec of all langs obtained with script.langs()
        let script_langs: Vec<Lang> = Script::all()
            .iter()
            .map(|script| script.langs())
            .flatten()
            .copied()
            .collect();

        // Ensure all langs belong at least to one script
        for lang in Lang::all() {
            assert!(script_langs.contains(&lang));
        }
    }

    #[test]
    fn test_script_display() {
        assert_eq!(Script::Georgian.to_string(), "Georgian");
        assert_eq!(Script::Cyrillic.to_string(), "Cyrillic");
        assert_eq!(Script::Arabic.to_string(), "Arabic");
    }
}
<commit_msg>Impl Hash for Script<commit_after>use std::fmt;
use std::str::FromStr;

use super::lang_mapping;
use crate::error::ParseError;
use crate::Lang;

/// Represents a writing system (Latin, Cyrillic, Arabic, etc).
#[cfg_attr(feature = "enum-map", derive(::enum_map::Enum))]
#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub enum Script {
    // Keep this in alphabetic order (for C bindings)
    Arabic,
    Armenian,
    Bengali,
    Cyrillic,
    Devanagari,
    Ethiopic,
    Georgian,
    Greek,
    Gujarati,
    Gurmukhi,
    Hangul,
    Hebrew,
    Hiragana,
    Kannada,
    Katakana,
    Khmer,
    Latin,
    Malayalam,
    Mandarin,
    Myanmar,
    Oriya,
    Sinhala,
    Tamil,
    Telugu,
    Thai,
}

// Array of all existing Script values.
const VALUES: [Script; 25] = [
    Script::Arabic,
    Script::Armenian,
    Script::Bengali,
    Script::Cyrillic,
    Script::Devanagari,
    Script::Ethiopic,
    Script::Georgian,
    Script::Greek,
    Script::Gujarati,
    Script::Gurmukhi,
    Script::Hangul,
    Script::Hebrew,
    Script::Hiragana,
    Script::Kannada,
    Script::Katakana,
    Script::Khmer,
    Script::Latin,
    Script::Malayalam,
    Script::Mandarin,
    Script::Myanmar,
    Script::Oriya,
    Script::Sinhala,
    Script::Tamil,
    Script::Telugu,
    Script::Thai,
];

impl Script {
    /// Get all existing scripts.
    ///
    /// # Example
    /// ```
    /// use whatlang::Script;
    /// for script in Script::all() {
    ///     println!("{}", script);
    /// }
    /// ```
    pub fn all() -> &'static [Script] {
        &VALUES
    }

    pub fn name(&self) -> &str {
        match *self {
            Script::Latin => "Latin",
            Script::Cyrillic => "Cyrillic",
            Script::Arabic => "Arabic",
            Script::Devanagari => "Devanagari",
            Script::Hiragana => "Hiragana",
            Script::Katakana => "Katakana",
            Script::Ethiopic => "Ethiopic",
            Script::Hebrew => "Hebrew",
            Script::Bengali => "Bengali",
            Script::Georgian => "Georgian",
            Script::Mandarin => "Mandarin",
            Script::Hangul => "Hangul",
            Script::Greek => "Greek",
            Script::Kannada => "Kannada",
            Script::Tamil => "Tamil",
            Script::Thai => "Thai",
            Script::Gujarati => "Gujarati",
            Script::Gurmukhi => "Gurmukhi",
            Script::Telugu => "Telugu",
            Script::Malayalam => "Malayalam",
            Script::Oriya => "Oriya",
            Script::Myanmar => "Myanmar",
            Script::Sinhala => "Sinhala",
            Script::Khmer => "Khmer",
            Script::Armenian => "Armenian",
        }
    }

    pub fn langs(&self) -> &[Lang] {
        lang_mapping::script_langs(*self)
    }
}

impl fmt::Display for Script {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl FromStr for Script {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "latin" => Ok(Script::Latin),
            "cyrillic" => Ok(Script::Cyrillic),
            "arabic" => Ok(Script::Arabic),
            "devanagari" => Ok(Script::Devanagari),
            "hiragana" => Ok(Script::Hiragana),
            "katakana" => Ok(Script::Katakana),
            "ethiopic" => Ok(Script::Ethiopic),
            "hebrew" => Ok(Script::Hebrew),
            "bengali" => Ok(Script::Bengali),
            "georgian" => Ok(Script::Georgian),
            "mandarin" => Ok(Script::Mandarin),
            "hangul" => Ok(Script::Hangul),
            "greek" => Ok(Script::Greek),
            "kannada" => Ok(Script::Kannada),
            "tamil" => Ok(Script::Tamil),
            "thai" => Ok(Script::Thai),
            "gujarati" => Ok(Script::Gujarati),
            "gurmukhi" => Ok(Script::Gurmukhi),
            "telugu" => Ok(Script::Telugu),
            "malayalam" => Ok(Script::Malayalam),
            "oriya" => Ok(Script::Oriya),
            "myanmar" => Ok(Script::Myanmar),
            "sinhala" => Ok(Script::Sinhala),
            "khmer" => Ok(Script::Khmer),
            "armenian" => Ok(Script::Armenian),
            _ => Err(ParseError::Script(s.to_string())),
        }
    }
}

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

    #[test]
    fn test_all() {
        assert_eq!(Script::all().len(), 25);
        let all = Script::all();
        assert!(all.contains(&Script::Cyrillic));
        assert!(all.contains(&Script::Arabic));
        assert!(all.contains(&Script::Latin));
    }

    #[test]
    fn test_from_str() {
        for &script in Script::all() {
            let s = script.name();
            assert_eq!(s.parse::<Script>().unwrap(), script);
            assert_eq!(s.to_lowercase().parse::<Script>().unwrap(), script);
            assert_eq!(s.to_uppercase().parse::<Script>().unwrap(), script);
        }

        let result = "foobar".parse::<Script>();
        assert!(matches!(result, Err(ParseError::Script(_))));
    }

    #[test]
    fn test_langs() {
        // Vec of all langs obtained with script.langs()
        let script_langs: Vec<Lang> = Script::all()
            .iter()
            .map(|script| script.langs())
            .flatten()
            .copied()
            .collect();

        // Ensure all langs belong at least to one script
        for lang in Lang::all() {
            assert!(script_langs.contains(&lang));
        }
    }

    #[test]
    fn test_script_display() {
        assert_eq!(Script::Georgian.to_string(), "Georgian");
        assert_eq!(Script::Cyrillic.to_string(), "Cyrillic");
        assert_eq!(Script::Arabic.to_string(), "Arabic");
    }
}
<|endoftext|>


<commit_before>//!
//! A binding for the library `SDL2_image`
//!
//!
//! Note that you need to build with the
//! feature `image` for this module to be enabled,
//! like so:
//!
//! ```bash
//! $ cargo build --features "image"
//! ```
//!
//! If you want to use this with from inside your own
//! crate, you will need to add this in your Cargo.toml
//!
//! ```toml
//! [dependencies.sdl2]
//! version = ...
//! default-features = false
//! features = ["image"]
//! ```

use std::os::raw::{c_int, c_char};
use std::ffi::CString;
use std::path::Path;
use surface::Surface;
use render::{TextureCreator, Texture};
use rwops::RWops;
use version::Version;
use get_error;
use sys;
use sys::image;

bitflags! {
    /// InitFlags are passed to init() to control which subsystem
    /// functionality to load.
    pub struct InitFlag : u32 {
        const JPG  = image::IMG_InitFlags_IMG_INIT_JPG as u32;
        const PNG  = image::IMG_InitFlags_IMG_INIT_PNG as u32;
        const TIF  = image::IMG_InitFlags_IMG_INIT_TIF as u32;
        const WEBP = image::IMG_InitFlags_IMG_INIT_WEBP as u32;
    }
}

// This is used for error message for init
impl ::std::fmt::Display for InitFlag {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        if self.contains(InitFlag::JPG) {
            f.write_str("INIT_JPG ")?;
        }
        if self.contains(InitFlag::PNG) {
            f.write_str("INIT_PNG ")?;
        }
        if self.contains(InitFlag::TIF) {
            f.write_str("INIT_TIF ")?;
        }
        if self.contains(InitFlag::WEBP) {
            f.write_str("INIT_WEBP ")?;
        }
        Ok(())
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface: Sized {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is Result<~Surface, String>.
    fn from_file<P: AsRef<Path>>(filename: P) -> Result<Self, String>;
    fn from_xpm_array(xpm: *const *const i8) -> Result<Self, String>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save<P: AsRef<Path>>(&self, filename: P) -> Result<(), String>;
    fn save_rw(&self, dst: &mut RWops) -> Result<(), String>;
}

impl<'a> LoadSurface for Surface<'a> {
    fn from_file<P: AsRef<Path>>(filename: P) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let raw = image::IMG_Load(c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = image::IMG_ReadXPMFromArray(xpm as *mut *mut c_char);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }
}

impl<'a> SaveSurface for Surface<'a> {
    fn save<P: AsRef<Path>>(&self, filename: P) -> Result<(), String> {
        //! Saves an SDL Surface to a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let status = image::IMG_SavePNG(self.raw(), c_filename.as_ptr() as *const _);
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> Result<(), String> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = image::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a `TextureCreator`
pub trait LoadTexture {
    fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String>;
}

impl<T> LoadTexture for TextureCreator<T> {
    fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String> {
        //! Loads an SDL Texture from a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let raw = image::IMG_LoadTexture(self.raw(), c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(self.raw_create_texture(raw))
            }
        }
    }
}

/// Context manager for `sdl2_image` to manage quitting. Can't do much with it but
/// keep it alive while you are using it.
pub struct Sdl2ImageContext;

impl Drop for Sdl2ImageContext {
    fn drop(&mut self) {
        unsafe {
            image::IMG_Quit();
        }
    }
}

/// Initializes `SDL2_image` with `InitFlags`.
/// If not every flag is set it returns an error
pub fn init(flags: InitFlag) -> Result<Sdl2ImageContext, String> {
    let return_flags = unsafe {
        let used = image::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    };
    if !flags.intersects(return_flags) {
        // According to docs, error message text is not always set
        let mut error = get_error();
        if error.is_empty() {
            let un_init_flags = return_flags ^ flags;
            error = format!("Could not init: {}", un_init_flags);
            let _ = ::set_error(&error);
        }
        Err(error)
    } else {
        Ok(Sdl2ImageContext)
    }
}

/// Returns the version of the dynamically linked `SDL_image` library
pub fn get_linked_version() -> Version {
    unsafe { Version::from_ll(*image::IMG_Linked_Version()) }
}

#[inline]
fn to_surface_result<'a>(raw: *mut sys::SDL_Surface) -> Result<Surface<'a>, String> {
    if (raw as *mut ()).is_null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> Result<Surface<'static>, String>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> Result<Surface<'static>, String>;

    fn load_cur(&self) -> Result<Surface<'static>, String>;
    fn load_ico(&self) -> Result<Surface<'static>, String>;
    fn load_bmp(&self) -> Result<Surface<'static>, String>;
    fn load_pnm(&self) -> Result<Surface<'static>, String>;
    fn load_xpm(&self) -> Result<Surface<'static>, String>;
    fn load_xcf(&self) -> Result<Surface<'static>, String>;
    fn load_pcx(&self) -> Result<Surface<'static>, String>;
    fn load_gif(&self) -> Result<Surface<'static>, String>;
    fn load_jpg(&self) -> Result<Surface<'static>, String>;
    fn load_tif(&self) -> Result<Surface<'static>, String>;
    fn load_png(&self) -> Result<Surface<'static>, String>;
    fn load_tga(&self) -> Result<Surface<'static>, String>;
    fn load_lbm(&self) -> Result<Surface<'static>, String>;
    fn load_xv(&self) -> Result<Surface<'static>, String>;
    fn load_webp(&self) -> Result<Surface<'static>, String>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self) -> bool;
    fn is_webp(&self) -> bool;
}

impl<'a> ImageRWops for RWops<'a> {
    fn load(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_Load_RW(self.raw(), 0) };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> Result<Surface<'static>, String> {
        let raw = unsafe {
            let c_type = CString::new(_type.as_bytes()).unwrap();
            image::IMG_LoadTyped_RW(self.raw(), 0, c_type.as_ptr() as *const _)
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { image::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { image::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { image::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { image::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { image::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { image::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { image::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { image::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { image::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { image::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { image::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { image::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self) -> bool {
        unsafe { image::IMG_isXV(self.raw()) == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { image::IMG_isWEBP(self.raw()) == 1 }
    }
}
<commit_msg>add load_texture_rw interface for load texture from buffer<commit_after>//!
//! A binding for the library `SDL2_image`
//!
//!
//! Note that you need to build with the
//! feature `image` for this module to be enabled,
//! like so:
//!
//! ```bash
//! $ cargo build --features "image"
//! ```
//!
//! If you want to use this with from inside your own
//! crate, you will need to add this in your Cargo.toml
//!
//! ```toml
//! [dependencies.sdl2]
//! version = ...
//! default-features = false
//! features = ["image"]
//! ```

use std::os::raw::{c_int, c_char};
use std::ffi::CString;
use std::path::Path;
use surface::Surface;
use render::{TextureCreator, Texture};
use rwops::RWops;
use version::Version;
use get_error;
use sys;
use sys::image;

bitflags! {
    /// InitFlags are passed to init() to control which subsystem
    /// functionality to load.
    pub struct InitFlag : u32 {
        const JPG  = image::IMG_InitFlags_IMG_INIT_JPG as u32;
        const PNG  = image::IMG_InitFlags_IMG_INIT_PNG as u32;
        const TIF  = image::IMG_InitFlags_IMG_INIT_TIF as u32;
        const WEBP = image::IMG_InitFlags_IMG_INIT_WEBP as u32;
    }
}

// This is used for error message for init
impl ::std::fmt::Display for InitFlag {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        if self.contains(InitFlag::JPG) {
            f.write_str("INIT_JPG ")?;
        }
        if self.contains(InitFlag::PNG) {
            f.write_str("INIT_PNG ")?;
        }
        if self.contains(InitFlag::TIF) {
            f.write_str("INIT_TIF ")?;
        }
        if self.contains(InitFlag::WEBP) {
            f.write_str("INIT_WEBP ")?;
        }
        Ok(())
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface: Sized {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is Result<~Surface, String>.
    fn from_file<P: AsRef<Path>>(filename: P) -> Result<Self, String>;
    fn from_xpm_array(xpm: *const *const i8) -> Result<Self, String>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save<P: AsRef<Path>>(&self, filename: P) -> Result<(), String>;
    fn save_rw(&self, dst: &mut RWops) -> Result<(), String>;
}

impl<'a> LoadSurface for Surface<'a> {
    fn from_file<P: AsRef<Path>>(filename: P) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let raw = image::IMG_Load(c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = image::IMG_ReadXPMFromArray(xpm as *mut *mut c_char);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }
}

impl<'a> SaveSurface for Surface<'a> {
    fn save<P: AsRef<Path>>(&self, filename: P) -> Result<(), String> {
        //! Saves an SDL Surface to a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let status = image::IMG_SavePNG(self.raw(), c_filename.as_ptr() as *const _);
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> Result<(), String> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = image::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a `TextureCreator`
pub trait LoadTexture {
    fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String>;
    fn load_texture_rw(&self, buf: &[u8]) -> Result<Texture, String>;
}

impl<T> LoadTexture for TextureCreator<T> {
    fn load_texture<P: AsRef<Path>>(&self, filename: P) -> Result<Texture, String> {
        //! Loads an SDL Texture from a file
        unsafe {
            let c_filename = CString::new(filename.as_ref().to_str().unwrap()).unwrap();
            let raw = image::IMG_LoadTexture(self.raw(), c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(self.raw_create_texture(raw))
            }
        }
    }

    fn load_texture_rw(&self, buf: &[u8]) -> Result<Texture, String> {
        unsafe {
            let buf = sdl2_sys::SDL_RWFromMem(buf.as_ptr() as *mut libc::c_void, buf.len() as i32);
            let raw = image::IMG_LoadTexture_RW(self.raw(), buf, 1); // close(free) buff after load
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(self.raw_create_texture(raw))
            }
        }
    }
}

/// Context manager for `sdl2_image` to manage quitting. Can't do much with it but
/// keep it alive while you are using it.
pub struct Sdl2ImageContext;

impl Drop for Sdl2ImageContext {
    fn drop(&mut self) {
        unsafe {
            image::IMG_Quit();
        }
    }
}

/// Initializes `SDL2_image` with `InitFlags`.
/// If not every flag is set it returns an error
pub fn init(flags: InitFlag) -> Result<Sdl2ImageContext, String> {
    let return_flags = unsafe {
        let used = image::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    };
    if !flags.intersects(return_flags) {
        // According to docs, error message text is not always set
        let mut error = get_error();
        if error.is_empty() {
            let un_init_flags = return_flags ^ flags;
            error = format!("Could not init: {}", un_init_flags);
            let _ = ::set_error(&error);
        }
        Err(error)
    } else {
        Ok(Sdl2ImageContext)
    }
}

/// Returns the version of the dynamically linked `SDL_image` library
pub fn get_linked_version() -> Version {
    unsafe { Version::from_ll(*image::IMG_Linked_Version()) }
}

#[inline]
fn to_surface_result<'a>(raw: *mut sys::SDL_Surface) -> Result<Surface<'a>, String> {
    if (raw as *mut ()).is_null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> Result<Surface<'static>, String>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> Result<Surface<'static>, String>;

    fn load_cur(&self) -> Result<Surface<'static>, String>;
    fn load_ico(&self) -> Result<Surface<'static>, String>;
    fn load_bmp(&self) -> Result<Surface<'static>, String>;
    fn load_pnm(&self) -> Result<Surface<'static>, String>;
    fn load_xpm(&self) -> Result<Surface<'static>, String>;
    fn load_xcf(&self) -> Result<Surface<'static>, String>;
    fn load_pcx(&self) -> Result<Surface<'static>, String>;
    fn load_gif(&self) -> Result<Surface<'static>, String>;
    fn load_jpg(&self) -> Result<Surface<'static>, String>;
    fn load_tif(&self) -> Result<Surface<'static>, String>;
    fn load_png(&self) -> Result<Surface<'static>, String>;
    fn load_tga(&self) -> Result<Surface<'static>, String>;
    fn load_lbm(&self) -> Result<Surface<'static>, String>;
    fn load_xv(&self) -> Result<Surface<'static>, String>;
    fn load_webp(&self) -> Result<Surface<'static>, String>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self) -> bool;
    fn is_webp(&self) -> bool;
}

impl<'a> ImageRWops for RWops<'a> {
    fn load(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_Load_RW(self.raw(), 0) };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> Result<Surface<'static>, String> {
        let raw = unsafe {
            let c_type = CString::new(_type.as_bytes()).unwrap();
            image::IMG_LoadTyped_RW(self.raw(), 0, c_type.as_ptr() as *const _)
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self) -> Result<Surface<'static>, String> {
        let raw = unsafe { image::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { image::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { image::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { image::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { image::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { image::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { image::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { image::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { image::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { image::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { image::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { image::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { image::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self) -> bool {
        unsafe { image::IMG_isXV(self.raw()) == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { image::IMG_isWEBP(self.raw()) == 1 }
    }
}
<|endoftext|>


<commit_before>//! Symbol- and name-storage facilities.
//!
//! This is based on [Servo's string-cache
//! library](https://github.com/servo/string-cache); at the moment it's very
//! lifetime-unsafe for `Pooled` instances.
use std;
use std::fmt;
use std::slice::bytes;
use std::hash::{hash, Hash, Hasher, SipHasher};
use std::collections::BTreeMap;
use std::borrow::ToOwned;

#[macro_use]
pub mod nameable;
pub use self::nameable::Nameable;

/// Methods required of `unpacked` types.
pub trait PackFormat {
    /// Pack an unpacked symbol in the implementing format into a generic
    /// Symbol object.
    fn pack(&self) -> Symbol;

    /// Unpack a generic Symbol into the implementing format.  This function is
    /// marked as `unsafe` because the caller must verify that the symbol is
    /// indeed packed by the receiver's implementation.
    unsafe fn unpack(sym: &Symbol) -> Self;

    /// Fetch a `&str` slice from a symbol packed in the implementing format.
    /// This function is marked as `unsafe` because the caller must verify that
    /// the symbol is indeed packed by the receiver's implementation.
    unsafe fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str;
}

/* ****************************************************************
 * Pool
 */
/// A collection of symbols.
#[derive(Debug)]
pub struct Pool {
    map:  BTreeMap<u64,String>
}

impl Pool {
    /// Create a new symbol pool.
    pub fn new() -> Pool {
        Pool{refcount: 1, map: BTreeMap::new()}
    }
    /// Fetch a symbol corresponding to the given string.  If if
    pub fn symbol(&mut self, name: &str) -> Symbol {
        if name.len() <= INLINE_SYMBOL_MAX_LEN
        { Inline::new(name).pack() }
        else { let hsh = hash::<_, SipHasher>(&name) << 1;
               if ! self.map.contains_key(&hsh) {
                   self.map.insert(hsh, name.to_owned());
               }

               Pooled::new(hsh, self).pack()
        }
    }
}

/* ****************************************************************
 * Symbol
 */
/// An atomic `Copy`able string.  Symbol either encodes a short string
/// directly, or stores it in an external Pool.
#[derive(Debug)]
pub struct Symbol {
    value: [u64; 2]
}
impl Copy for Symbol {}

impl Symbol {
    /// Convert the symbol to its unpacked representation.
    #[inline(always)]
    pub fn unpack(&self) -> Unpacked
    { unsafe { <Unpacked as PackFormat>::unpack(self) } }

    /// Get symbol's pack format.
    #[inline(always)]
    pub fn type_of(&self) -> Type {
        match (self.value[0] & 0x01) as u8 {
            INLINE => Type::INLINE,
            POOLED => Type::POOLED,
            _ => unreachable!()
        }
    }

}

impl std::str::Str for Symbol {
    fn as_slice<'t>(&'t self) -> &'t str { unsafe { <Unpacked as PackFormat>::as_slice_from(self) } }
}

impl Nameable for Symbol {
    fn name(&self) -> Symbol { *self }
}

impl Hash for Symbol {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_slice())
    }
}

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

/* ****************************************************************
 * Inline
 */
const INLINE_SYMBOL_MAX_LEN: usize = 15;//std::mem::size_of::<Symbol>() - 1;

/// Data format for symbols packed entirely into a `Symbol` instance.
#[derive(Debug)]
pub struct Inline{len: u8, data: [u8; INLINE_SYMBOL_MAX_LEN]}

impl Inline {
    /// Create a new Inline-format symbol using the given string.
    pub fn new(name: &str) -> Self {
        panic_unless!(name.len() <= INLINE_SYMBOL_MAX_LEN,
                      "Argument to Inline::new (length {}) exceeds maximum length ({}) for an inlined symbol",
                      name.len(), INLINE_SYMBOL_MAX_LEN);
        let mut buf: [u8; INLINE_SYMBOL_MAX_LEN] = [0; INLINE_SYMBOL_MAX_LEN];

        bytes::copy_memory(buf.as_mut_slice(), name.as_bytes());
        Inline{len: name.len() as u8, data: buf}
    }
}

impl std::str::Str for Inline {
    fn as_slice<'t>(&'t self) -> &'t str {
        let src: &[u8] = self.data.as_slice();
        std::str::from_utf8(&src[..(self.len as usize)]).unwrap()
    }
}

impl PackFormat for Inline {
    // Convert the Inline symbol into a generic packed representation.
    fn pack(&self) -> Symbol {
        unsafe {
            let mut out = Symbol{ value: [0; 2] };
            let mut dest: &mut [u8; 16] = std::mem::transmute(&mut out.value);
            dest[0] = (self.len << 1) | INLINE;
            bytes::copy_memory(&mut dest[1..], &self.data);
            out
        }
    }
    fn unpack(sym: &Symbol) -> Self {
        let mut out = Inline{len: 0, data: [0; INLINE_SYMBOL_MAX_LEN]};
        let src: &[u8;16] =
            unsafe { std::mem::transmute(&sym.value) };

        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        out.len = src[0] >> 1;
        panic_unless!(out.len <= INLINE_SYMBOL_MAX_LEN as u8,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      out.len, INLINE_SYMBOL_MAX_LEN);
        bytes::copy_memory(out.data.as_mut_slice(), &src[1..(out.len as usize + 1)]);

        out
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        let src: &[u8; 16] = unsafe { std::mem::transmute(&sym.value) };
        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        let len: usize = (src[0] >> 1) as usize;
        panic_unless!(len <= INLINE_SYMBOL_MAX_LEN,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      len, INLINE_SYMBOL_MAX_LEN);

        std::str::from_utf8(&src[1..(len + 1)]).unwrap()
    }
}

impl std::cmp::Eq for Inline {}
impl std::cmp::PartialEq for Inline {
    fn eq(&self, other: &Self) -> bool {
        self.len == other.len && self.data == other.data
    }
}

/* ****************************************************************
 * Pooled
 */
/// Data format for symbols stored as references to a symbol pool.
#[derive(Debug)]
pub struct Pooled{key: u64, pool: *const Pool}

impl Pooled {
    #[inline(always)]
    pub fn new(key: u64, pool: *const Pool) -> Self {
        Pooled{key: key, pool: pool}
    }

}

impl std::str::Str for Pooled {
    fn as_slice<'u>(&'u self) -> &'u str {
        unsafe { (*self.pool).map[self.key].as_slice() }
    }
}

impl PackFormat for Pooled {
    fn pack(&self) -> Symbol {
        let mut val: [u64; 2] = [0; 2];
        val[0] = self.key  | POOLED as u64;
        val[1] = self.pool as u64;
        Symbol{value: val}
    }
    fn unpack(sym: &Symbol) -> Self {
        Pooled{key: sym.value[0],
               pool: unsafe { std::mem::transmute(sym.value[1] as *const Pool) }}
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        panic_unless!(sym.value[0] & TAG_MASK as u64 == POOLED as u64,
                      "Invalid flag bit for pooled symbol");
        unsafe { std::mem::transmute::<_,&'t Pool>(sym.value[1] as *const Pool) }.
            map[sym.value[0]].as_slice()
    }
}

impl std::cmp::Eq for Pooled {}
impl std::cmp::PartialEq for Pooled {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key && self.pool as u64 == other.pool as u64
    }
}

/* ****************************************************************
 * Types
 */
const TAG_MASK: u8 = 0x01;
const POOLED: u8 = 0;
const INLINE: u8 = 1;

pub enum Type { INLINE = 0, POOLED = 1 }

impl std::cmp::Eq for Type {}
impl std::cmp::PartialEq for Type {
    fn eq(&self, other: &Self) -> bool {
        match *self { Type::INLINE => match *other { Type::INLINE => true, _ => false },
                      Type::POOLED => match *other { Type::POOLED => true, _ => false } }
    }
}

impl std::fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { Type::INLINE => write!(f, "inline"),
                      Type::POOLED => write!(f, "pooled") }
    }
}

/* ****************************************************************
 * Unpacked
 */

/// A differentiated `Symbol`.
#[derive(Debug)]
pub enum Unpacked {
    /// Symbol stored inline in `Symbol`'s data fields.
    Inline(Inline),

    /// Symbol stored in a Pool
    Pooled(Pooled)
}

impl PackFormat for Unpacked {
    fn pack(&self) -> Symbol {
        match *self {
            Unpacked::Inline(ref x) => x.pack(),
            Unpacked::Pooled(ref x) => x.pack()
        }
    }

    fn unpack(sym: &Symbol) -> Unpacked {
        match sym.type_of() {
            Type::POOLED => Unpacked::Pooled(unsafe { <Pooled as PackFormat>::unpack(sym) }),
            Type::INLINE => Unpacked::Inline(unsafe { <Inline as PackFormat>::unpack(sym) }),
        }
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        match sym.type_of() {
            Type::INLINE => unsafe { <Inline as PackFormat>::as_slice_from(sym) },
            Type::POOLED => unsafe { <Pooled as PackFormat>::as_slice_from(sym) }
        }
    }
}

impl std::str::Str for Unpacked {
    fn as_slice<'t>(&'t self) -> &'t str {
        match *self {
            Unpacked::Inline(ref x) => x.as_slice(),
            Unpacked::Pooled(ref x) => x.as_slice()
        }
    }
}

impl std::cmp::Eq for Unpacked {}
impl std::cmp::PartialEq for Unpacked {
    fn eq(&self, other: &Self) -> bool {
        match *self {
            Unpacked::Inline(ref si) =>
                match *other { Unpacked::Inline(ref oi) => si == oi, _ => false },
            Unpacked::Pooled(ref sp) =>
                match *other { Unpacked::Pooled(ref op) => sp == op, _ => false }
        }
    }
}

impl Unpacked {
}
<commit_msg>symbol::Pool: remove initializer for field removed in @60442e9<commit_after>//! Symbol- and name-storage facilities.
//!
//! This is based on [Servo's string-cache
//! library](https://github.com/servo/string-cache); at the moment it's very
//! lifetime-unsafe for `Pooled` instances.
use std;
use std::fmt;
use std::slice::bytes;
use std::hash::{hash, Hash, Hasher, SipHasher};
use std::collections::BTreeMap;
use std::borrow::ToOwned;

#[macro_use]
pub mod nameable;
pub use self::nameable::Nameable;

/// Methods required of `unpacked` types.
pub trait PackFormat {
    /// Pack an unpacked symbol in the implementing format into a generic
    /// Symbol object.
    fn pack(&self) -> Symbol;

    /// Unpack a generic Symbol into the implementing format.  This function is
    /// marked as `unsafe` because the caller must verify that the symbol is
    /// indeed packed by the receiver's implementation.
    unsafe fn unpack(sym: &Symbol) -> Self;

    /// Fetch a `&str` slice from a symbol packed in the implementing format.
    /// This function is marked as `unsafe` because the caller must verify that
    /// the symbol is indeed packed by the receiver's implementation.
    unsafe fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str;
}

/* ****************************************************************
 * Pool
 */
/// A collection of symbols.
#[derive(Debug)]
pub struct Pool {
    map:  BTreeMap<u64,String>
}

impl Pool {
    /// Create a new symbol pool.
    pub fn new() -> Pool {
        Pool{map: BTreeMap::new()}
    }
    /// Fetch a symbol corresponding to the given string.  If if
    pub fn symbol(&mut self, name: &str) -> Symbol {
        if name.len() <= INLINE_SYMBOL_MAX_LEN
        { Inline::new(name).pack() }
        else { let hsh = hash::<_, SipHasher>(&name) << 1;
               if ! self.map.contains_key(&hsh) {
                   self.map.insert(hsh, name.to_owned());
               }

               Pooled::new(hsh, self).pack()
        }
    }
}

/* ****************************************************************
 * Symbol
 */
/// An atomic `Copy`able string.  Symbol either encodes a short string
/// directly, or stores it in an external Pool.
#[derive(Debug)]
pub struct Symbol {
    value: [u64; 2]
}
impl Copy for Symbol {}

impl Symbol {
    /// Convert the symbol to its unpacked representation.
    #[inline(always)]
    pub fn unpack(&self) -> Unpacked
    { unsafe { <Unpacked as PackFormat>::unpack(self) } }

    /// Get symbol's pack format.
    #[inline(always)]
    pub fn type_of(&self) -> Type {
        match (self.value[0] & 0x01) as u8 {
            INLINE => Type::INLINE,
            POOLED => Type::POOLED,
            _ => unreachable!()
        }
    }

}

impl std::str::Str for Symbol {
    fn as_slice<'t>(&'t self) -> &'t str { unsafe { <Unpacked as PackFormat>::as_slice_from(self) } }
}

impl Nameable for Symbol {
    fn name(&self) -> Symbol { *self }
}

impl Hash for Symbol {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_slice())
    }
}

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

/* ****************************************************************
 * Inline
 */
const INLINE_SYMBOL_MAX_LEN: usize = 15;//std::mem::size_of::<Symbol>() - 1;

/// Data format for symbols packed entirely into a `Symbol` instance.
#[derive(Debug)]
pub struct Inline{len: u8, data: [u8; INLINE_SYMBOL_MAX_LEN]}

impl Inline {
    /// Create a new Inline-format symbol using the given string.
    pub fn new(name: &str) -> Self {
        panic_unless!(name.len() <= INLINE_SYMBOL_MAX_LEN,
                      "Argument to Inline::new (length {}) exceeds maximum length ({}) for an inlined symbol",
                      name.len(), INLINE_SYMBOL_MAX_LEN);
        let mut buf: [u8; INLINE_SYMBOL_MAX_LEN] = [0; INLINE_SYMBOL_MAX_LEN];

        bytes::copy_memory(buf.as_mut_slice(), name.as_bytes());
        Inline{len: name.len() as u8, data: buf}
    }
}

impl std::str::Str for Inline {
    fn as_slice<'t>(&'t self) -> &'t str {
        let src: &[u8] = self.data.as_slice();
        std::str::from_utf8(&src[..(self.len as usize)]).unwrap()
    }
}

impl PackFormat for Inline {
    // Convert the Inline symbol into a generic packed representation.
    fn pack(&self) -> Symbol {
        unsafe {
            let mut out = Symbol{ value: [0; 2] };
            let mut dest: &mut [u8; 16] = std::mem::transmute(&mut out.value);
            dest[0] = (self.len << 1) | INLINE;
            bytes::copy_memory(&mut dest[1..], &self.data);
            out
        }
    }
    fn unpack(sym: &Symbol) -> Self {
        let mut out = Inline{len: 0, data: [0; INLINE_SYMBOL_MAX_LEN]};
        let src: &[u8;16] =
            unsafe { std::mem::transmute(&sym.value) };

        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        out.len = src[0] >> 1;
        panic_unless!(out.len <= INLINE_SYMBOL_MAX_LEN as u8,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      out.len, INLINE_SYMBOL_MAX_LEN);
        bytes::copy_memory(out.data.as_mut_slice(), &src[1..(out.len as usize + 1)]);

        out
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        let src: &[u8; 16] = unsafe { std::mem::transmute(&sym.value) };
        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        let len: usize = (src[0] >> 1) as usize;
        panic_unless!(len <= INLINE_SYMBOL_MAX_LEN,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      len, INLINE_SYMBOL_MAX_LEN);

        std::str::from_utf8(&src[1..(len + 1)]).unwrap()
    }
}

impl std::cmp::Eq for Inline {}
impl std::cmp::PartialEq for Inline {
    fn eq(&self, other: &Self) -> bool {
        self.len == other.len && self.data == other.data
    }
}

/* ****************************************************************
 * Pooled
 */
/// Data format for symbols stored as references to a symbol pool.
#[derive(Debug)]
pub struct Pooled{key: u64, pool: *const Pool}

impl Pooled {
    #[inline(always)]
    pub fn new(key: u64, pool: *const Pool) -> Self {
        Pooled{key: key, pool: pool}
    }

}

impl std::str::Str for Pooled {
    fn as_slice<'u>(&'u self) -> &'u str {
        unsafe { (*self.pool).map[self.key].as_slice() }
    }
}

impl PackFormat for Pooled {
    fn pack(&self) -> Symbol {
        let mut val: [u64; 2] = [0; 2];
        val[0] = self.key  | POOLED as u64;
        val[1] = self.pool as u64;
        Symbol{value: val}
    }
    fn unpack(sym: &Symbol) -> Self {
        Pooled{key: sym.value[0],
               pool: unsafe { std::mem::transmute(sym.value[1] as *const Pool) }}
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        panic_unless!(sym.value[0] & TAG_MASK as u64 == POOLED as u64,
                      "Invalid flag bit for pooled symbol");
        unsafe { std::mem::transmute::<_,&'t Pool>(sym.value[1] as *const Pool) }.
            map[sym.value[0]].as_slice()
    }
}

impl std::cmp::Eq for Pooled {}
impl std::cmp::PartialEq for Pooled {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key && self.pool as u64 == other.pool as u64
    }
}

/* ****************************************************************
 * Types
 */
const TAG_MASK: u8 = 0x01;
const POOLED: u8 = 0;
const INLINE: u8 = 1;

pub enum Type { INLINE = 0, POOLED = 1 }

impl std::cmp::Eq for Type {}
impl std::cmp::PartialEq for Type {
    fn eq(&self, other: &Self) -> bool {
        match *self { Type::INLINE => match *other { Type::INLINE => true, _ => false },
                      Type::POOLED => match *other { Type::POOLED => true, _ => false } }
    }
}

impl std::fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { Type::INLINE => write!(f, "inline"),
                      Type::POOLED => write!(f, "pooled") }
    }
}

/* ****************************************************************
 * Unpacked
 */

/// A differentiated `Symbol`.
#[derive(Debug)]
pub enum Unpacked {
    /// Symbol stored inline in `Symbol`'s data fields.
    Inline(Inline),

    /// Symbol stored in a Pool
    Pooled(Pooled)
}

impl PackFormat for Unpacked {
    fn pack(&self) -> Symbol {
        match *self {
            Unpacked::Inline(ref x) => x.pack(),
            Unpacked::Pooled(ref x) => x.pack()
        }
    }

    fn unpack(sym: &Symbol) -> Unpacked {
        match sym.type_of() {
            Type::POOLED => Unpacked::Pooled(unsafe { <Pooled as PackFormat>::unpack(sym) }),
            Type::INLINE => Unpacked::Inline(unsafe { <Inline as PackFormat>::unpack(sym) }),
        }
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        match sym.type_of() {
            Type::INLINE => unsafe { <Inline as PackFormat>::as_slice_from(sym) },
            Type::POOLED => unsafe { <Pooled as PackFormat>::as_slice_from(sym) }
        }
    }
}

impl std::str::Str for Unpacked {
    fn as_slice<'t>(&'t self) -> &'t str {
        match *self {
            Unpacked::Inline(ref x) => x.as_slice(),
            Unpacked::Pooled(ref x) => x.as_slice()
        }
    }
}

impl std::cmp::Eq for Unpacked {}
impl std::cmp::PartialEq for Unpacked {
    fn eq(&self, other: &Self) -> bool {
        match *self {
            Unpacked::Inline(ref si) =>
                match *other { Unpacked::Inline(ref oi) => si == oi, _ => false },
            Unpacked::Pooled(ref sp) =>
                match *other { Unpacked::Pooled(ref op) => sp == op, _ => false }
        }
    }
}

impl Unpacked {
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use {Errno, Error, Result};
use libc::{c_void, c_long, siginfo_t};
use ::unistd::Pid;

pub mod ptrace {
    use libc::c_int;

    pub type PtraceRequest = c_int;

    pub const PTRACE_TRACEME:     PtraceRequest = 0;
    pub const PTRACE_PEEKTEXT:    PtraceRequest = 1;
    pub const PTRACE_PEEKDATA:    PtraceRequest = 2;
    pub const PTRACE_PEEKUSER:    PtraceRequest = 3;
    pub const PTRACE_POKETEXT:    PtraceRequest = 4;
    pub const PTRACE_POKEDATA:    PtraceRequest = 5;
    pub const PTRACE_POKEUSER:    PtraceRequest = 6;
    pub const PTRACE_CONT:        PtraceRequest = 7;
    pub const PTRACE_KILL:        PtraceRequest = 8;
    pub const PTRACE_SINGLESTEP:  PtraceRequest = 9;
    pub const PTRACE_GETREGS:     PtraceRequest = 12;
    pub const PTRACE_SETREGS:     PtraceRequest = 13;
    pub const PTRACE_GETFPREGS:   PtraceRequest = 14;
    pub const PTRACE_SETFPREGS:   PtraceRequest = 15;
    pub const PTRACE_ATTACH:      PtraceRequest = 16;
    pub const PTRACE_DETACH:      PtraceRequest = 17;
    pub const PTRACE_GETFPXREGS:  PtraceRequest = 18;
    pub const PTRACE_SETFPXREGS:  PtraceRequest = 19;
    pub const PTRACE_SYSCALL:     PtraceRequest = 24;
    pub const PTRACE_SETOPTIONS:  PtraceRequest = 0x4200;
    pub const PTRACE_GETEVENTMSG: PtraceRequest = 0x4201;
    pub const PTRACE_GETSIGINFO:  PtraceRequest = 0x4202;
    pub const PTRACE_SETSIGINFO:  PtraceRequest = 0x4203;
    pub const PTRACE_GETREGSET:   PtraceRequest = 0x4204;
    pub const PTRACE_SETREGSET:   PtraceRequest = 0x4205;
    pub const PTRACE_SEIZE:       PtraceRequest = 0x4206;
    pub const PTRACE_INTERRUPT:   PtraceRequest = 0x4207;
    pub const PTRACE_LISTEN:      PtraceRequest = 0x4208;
    pub const PTRACE_PEEKSIGINFO: PtraceRequest = 0x4209;

    pub type PtraceEvent = c_int;

    pub const PTRACE_EVENT_FORK:       PtraceEvent = 1;
    pub const PTRACE_EVENT_VFORK:      PtraceEvent = 2;
    pub const PTRACE_EVENT_CLONE:      PtraceEvent = 3;
    pub const PTRACE_EVENT_EXEC:       PtraceEvent = 4;
    pub const PTRACE_EVENT_VFORK_DONE: PtraceEvent = 5;
    pub const PTRACE_EVENT_EXIT:       PtraceEvent = 6;
    pub const PTRACE_EVENT_SECCOMP:    PtraceEvent = 6;
    pub const PTRACE_EVENT_STOP:       PtraceEvent = 128;

    pub type PtraceOptions = c_int;
    pub const PTRACE_O_TRACESYSGOOD: PtraceOptions   = 1;
    pub const PTRACE_O_TRACEFORK: PtraceOptions      = (1 << PTRACE_EVENT_FORK);
    pub const PTRACE_O_TRACEVFORK: PtraceOptions     = (1 << PTRACE_EVENT_VFORK);
    pub const PTRACE_O_TRACECLONE: PtraceOptions     = (1 << PTRACE_EVENT_CLONE);
    pub const PTRACE_O_TRACEEXEC: PtraceOptions      = (1 << PTRACE_EVENT_EXEC);
    pub const PTRACE_O_TRACEVFORKDONE: PtraceOptions = (1 << PTRACE_EVENT_VFORK_DONE);
    pub const PTRACE_O_TRACEEXIT: PtraceOptions      = (1 << PTRACE_EVENT_EXIT);
    pub const PTRACE_O_TRACESECCOMP: PtraceOptions   = (1 << PTRACE_EVENT_SECCOMP);
}

mod ffi {
    use libc::{pid_t, c_int, c_long, c_void};

    extern {
        pub fn ptrace(request: c_int, pid: pid_t, addr: * const c_void, data: * const c_void) -> c_long;
    }
}

/// Performs a ptrace request. If the request in question is provided by a specialised function
/// this function will return an unsupported operation error.
pub fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    use self::ptrace::*;

    match request {
        PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data),
        PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation),
        _ => ptrace_other(request, pid, addr, data)
    }
}

fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    let ret = unsafe {
        Errno::clear();
        ffi::ptrace(request, pid.into(), addr, data)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
        err @ Err(..) => err,
    }
}

/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than c_long
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
    // Creates an uninitialized pointer to store result in
    let data: T = unsafe { mem::uninitialized() };
    let res = unsafe { ffi::ptrace(request, pid.into(), ptr::null_mut(), &data as *const _ as *const c_void) };
    Errno::result(res)?;
    Ok(data)
}

fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    Errno::result(unsafe { ffi::ptrace(request, pid.into(), addr, data) }).map(|_| 0)
}

/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
pub fn ptrace_setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> {
    use self::ptrace::*;
    use std::ptr;

    let res = unsafe { ffi::ptrace(PTRACE_SETOPTIONS, pid.into(), ptr::null_mut(), options as *mut c_void) };
    Errno::result(res).map(|_| ())
}

/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
pub fn ptrace_getevent(pid: Pid) -> Result<c_long> {
    use self::ptrace::*;
    ptrace_get_data::<c_long>(PTRACE_GETEVENTMSG, pid)
}

/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
pub fn ptrace_getsiginfo(pid: Pid) -> Result<siginfo_t> {
    use self::ptrace::*;
    ptrace_get_data::<siginfo_t>(PTRACE_GETSIGINFO, pid)
}

/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
pub fn ptrace_setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
    use self::ptrace::*;
    let ret = unsafe{
        Errno::clear();
        ffi::ptrace(PTRACE_SETSIGINFO, pid.into(), ptr::null_mut(), sig as *const _ as *const c_void)
    };
    match Errno::result(ret) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}
<commit_msg>Rename the public ptrace_* functions.<commit_after>use std::{mem, ptr};
use {Errno, Error, Result};
use libc::{c_void, c_long, siginfo_t};
use ::unistd::Pid;

pub mod ptrace {
    use libc::c_int;

    pub type PtraceRequest = c_int;

    pub const PTRACE_TRACEME:     PtraceRequest = 0;
    pub const PTRACE_PEEKTEXT:    PtraceRequest = 1;
    pub const PTRACE_PEEKDATA:    PtraceRequest = 2;
    pub const PTRACE_PEEKUSER:    PtraceRequest = 3;
    pub const PTRACE_POKETEXT:    PtraceRequest = 4;
    pub const PTRACE_POKEDATA:    PtraceRequest = 5;
    pub const PTRACE_POKEUSER:    PtraceRequest = 6;
    pub const PTRACE_CONT:        PtraceRequest = 7;
    pub const PTRACE_KILL:        PtraceRequest = 8;
    pub const PTRACE_SINGLESTEP:  PtraceRequest = 9;
    pub const PTRACE_GETREGS:     PtraceRequest = 12;
    pub const PTRACE_SETREGS:     PtraceRequest = 13;
    pub const PTRACE_GETFPREGS:   PtraceRequest = 14;
    pub const PTRACE_SETFPREGS:   PtraceRequest = 15;
    pub const PTRACE_ATTACH:      PtraceRequest = 16;
    pub const PTRACE_DETACH:      PtraceRequest = 17;
    pub const PTRACE_GETFPXREGS:  PtraceRequest = 18;
    pub const PTRACE_SETFPXREGS:  PtraceRequest = 19;
    pub const PTRACE_SYSCALL:     PtraceRequest = 24;
    pub const PTRACE_SETOPTIONS:  PtraceRequest = 0x4200;
    pub const PTRACE_GETEVENTMSG: PtraceRequest = 0x4201;
    pub const PTRACE_GETSIGINFO:  PtraceRequest = 0x4202;
    pub const PTRACE_SETSIGINFO:  PtraceRequest = 0x4203;
    pub const PTRACE_GETREGSET:   PtraceRequest = 0x4204;
    pub const PTRACE_SETREGSET:   PtraceRequest = 0x4205;
    pub const PTRACE_SEIZE:       PtraceRequest = 0x4206;
    pub const PTRACE_INTERRUPT:   PtraceRequest = 0x4207;
    pub const PTRACE_LISTEN:      PtraceRequest = 0x4208;
    pub const PTRACE_PEEKSIGINFO: PtraceRequest = 0x4209;

    pub type PtraceEvent = c_int;

    pub const PTRACE_EVENT_FORK:       PtraceEvent = 1;
    pub const PTRACE_EVENT_VFORK:      PtraceEvent = 2;
    pub const PTRACE_EVENT_CLONE:      PtraceEvent = 3;
    pub const PTRACE_EVENT_EXEC:       PtraceEvent = 4;
    pub const PTRACE_EVENT_VFORK_DONE: PtraceEvent = 5;
    pub const PTRACE_EVENT_EXIT:       PtraceEvent = 6;
    pub const PTRACE_EVENT_SECCOMP:    PtraceEvent = 6;
    pub const PTRACE_EVENT_STOP:       PtraceEvent = 128;

    pub type PtraceOptions = c_int;
    pub const PTRACE_O_TRACESYSGOOD: PtraceOptions   = 1;
    pub const PTRACE_O_TRACEFORK: PtraceOptions      = (1 << PTRACE_EVENT_FORK);
    pub const PTRACE_O_TRACEVFORK: PtraceOptions     = (1 << PTRACE_EVENT_VFORK);
    pub const PTRACE_O_TRACECLONE: PtraceOptions     = (1 << PTRACE_EVENT_CLONE);
    pub const PTRACE_O_TRACEEXEC: PtraceOptions      = (1 << PTRACE_EVENT_EXEC);
    pub const PTRACE_O_TRACEVFORKDONE: PtraceOptions = (1 << PTRACE_EVENT_VFORK_DONE);
    pub const PTRACE_O_TRACEEXIT: PtraceOptions      = (1 << PTRACE_EVENT_EXIT);
    pub const PTRACE_O_TRACESECCOMP: PtraceOptions   = (1 << PTRACE_EVENT_SECCOMP);
}

mod ffi {
    use libc::{pid_t, c_int, c_long, c_void};

    extern {
        pub fn ptrace(request: c_int, pid: pid_t, addr: * const c_void, data: * const c_void) -> c_long;
    }
}

/// Performs a ptrace request. If the request in question is provided by a specialised function
/// this function will return an unsupported operation error.
pub fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    use self::ptrace::*;

    match request {
        PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data),
        PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation),
        _ => ptrace_other(request, pid, addr, data)
    }
}

fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    let ret = unsafe {
        Errno::clear();
        ffi::ptrace(request, pid.into(), addr, data)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
        err @ Err(..) => err,
    }
}

/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than c_long
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
    // Creates an uninitialized pointer to store result in
    let data: T = unsafe { mem::uninitialized() };
    let res = unsafe { ffi::ptrace(request, pid.into(), ptr::null_mut(), &data as *const _ as *const c_void) };
    Errno::result(res)?;
    Ok(data)
}

fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    Errno::result(unsafe { ffi::ptrace(request, pid.into(), addr, data) }).map(|_| 0)
}

/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
pub fn setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> {
    use self::ptrace::*;
    use std::ptr;

    let res = unsafe { ffi::ptrace(PTRACE_SETOPTIONS, pid.into(), ptr::null_mut(), options as *mut c_void) };
    Errno::result(res).map(|_| ())
}

/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
    use self::ptrace::*;
    ptrace_get_data::<c_long>(PTRACE_GETEVENTMSG, pid)
}

/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
    use self::ptrace::*;
    ptrace_get_data::<siginfo_t>(PTRACE_GETSIGINFO, pid)
}

/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
    use self::ptrace::*;
    let ret = unsafe{
        Errno::clear();
        ffi::ptrace(PTRACE_SETSIGINFO, pid.into(), ptr::null_mut(), sig as *const _ as *const c_void)
    };
    match Errno::result(ret) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}
<|endoftext|>