<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::{run, os};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::option::Option;
use std::io::{stdin, stdio, IoError, io_error, File, Truncate, Write};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use std::comm::Port;
use extra::getopts;
//use std::io::signal::{Listener, Interrupt, Signum};
use std::libc::types::os::arch::posix88::pid_t;

struct Cmd {
    program : ~str,
    argv : ~[~str],
}

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

trait Command {
    fn run(&mut self) -> Option<Process>;
}

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

    /*
    fn set_stdin(&mut self, stdin : Option<i32>) {
        self.stdin = stdin;
    }

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

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
    pid          : Option<i32>,
}
impl BackgroundProcess {
    fn new(cmd : Cmd) -> Option<BackgroundProcess> {
        if (cmd_exists(&cmd)) {
            Some(BackgroundProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                kill_chan: None,
                pid: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Kill signal ports; sent to struct.
        let (killport, killchan): (Port<int>, Chan<int>) = Chan::new();
        // Process ports; these don't leave this function.
        let (pport, pchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
            Some(mut process) => {
                pchan.try_send_deferred(Some(process.get_id()));
                let signal = killport.recv();
                let mut error = None;
                match signal {
                9 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                15 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                _ => {
                }}
                chan.try_send_deferred(process.finish());
            }
            None => {
                pchan.try_send_deferred(None);
            }}
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
        self.pid = pport.recv();
        self.pid
    }
}

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

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

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        loop {
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
            "" => {}
            _  => {
                self.push_hist(cmd_line)
            }}

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

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

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
            Some(ref kill_channel) => {
                kill_channel.try_send_deferred(15);
            }
            None => {
            }}
        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

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

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

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

    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        match Cmd::new(cmd_line) {
        Some(cmd) => {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        }
        None => {
            None
        }}
    }

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

    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match two {
            Some(mut p2) => {
                let output = p2.finish_with_output();
                if output.status.success() {
                    process.input().write(output.output);
                    process.close_input();
                    process.finish();
                }
                Some(~process)
            }
            None => {
                println("No two remain.");
                Some(~process)
            }}
        }
        None => {
            None
        }}
    }

    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BackgroundProcess::new(cmd) {
        Some(mut process) => {
            match process.run() {
            Some(pid) => {
                println!("{:s} {:i}", name, pid);
                self.processes.push(~process);
            }
            None => {
            }}
        }
        None => {
        }}
    }
}

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

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

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

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

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

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
    Some(ref p) => {
        match p.try_recv() {
        Some (exitstatus) => {
            dead = exitstatus.success();
        },
        None => {
        }}
    }
    _ => {
    }}
    dead
}

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

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    match CmdProcess::new(cmd, stdin, stdout) {
    Some(mut cmdprocess) => {
        cmdprocess.run()
    }
    None => {
        None
    }}
}

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

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

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

/*
    let mut listener = Listener::new();
    listener.register(Interrupt);
*/
/*
fn rupt(port: Port<Signum>) {
    do spawn {
        loop{
            match port.recv_opt() {
            Some(recv) => {
                match recv {
                Interrupt => {
                    println("Got Interrupt'ed");
                }
                _ => {
                    break;
                }}
            }
            None => {
            }}
        }
    }
}
*/

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

    match opt_cmd_line {
    Some(cmd_line) => {
        let mut shell = Shell::new("");
        shell.run_cmdline(cmd_line);
    }
    None => {
        Shell::new("gash > ").run();
    }}
}
<commit_msg>replace some logic with maybe<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::{run, os};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::option::Option;
use std::io::{stdin, stdio, IoError, io_error, File, Truncate, Write};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use std::comm::Port;
use extra::getopts;
//use std::io::signal::{Listener, Interrupt};
use std::libc::types::os::arch::posix88::pid_t;

struct Cmd {
    program : ~str,
    argv : ~[~str],
}

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

trait Command {
    fn run(&mut self) -> Option<Process>;
}

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

    /*
    fn set_stdin(&mut self, stdin : Option<i32>) {
        self.stdin = stdin;
    }

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

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
    pid          : Option<i32>,
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl BackgroundProcess {
    fn new(cmd : Cmd) -> Option<BackgroundProcess> {
        if (cmd_exists(&cmd)) {
            Some(BackgroundProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                kill_chan: None,
                pid: None,
                stdin: None,
                stdout: None,
            })
        }
        else { None }
    }

    fn new_fg(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<BackgroundProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(BackgroundProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                exit_port: None,
                kill_chan: None,
                pid: None,
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Kill signal ports; sent to struct.
        let (killport, killchan): (Port<int>, Chan<int>) = Chan::new();
        // Process ports; these don't leave this function.
        let (pport, pchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
            Some(mut process) => {
                // Send the pid out for the return value
                pchan.try_send_deferred(Some(process.get_id()));
                let signal = killport.recv();
                let mut error = None;
                match signal {
                9 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                15 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                _ => {
                }}
                chan.try_send_deferred(process.finish());
            }
            None => {
                pchan.try_send_deferred(None);
            }}
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
        self.pid = pport.recv();
        self.pid
    }
}

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

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

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        loop {
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
            "" => {}
            _  => {
                self.push_hist(cmd_line)
            }}

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

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

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
            Some(ref kill_channel) => {
                kill_channel.try_send_deferred(15);
            }
            None => {
            }}
        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

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

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

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

    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

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

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

    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match two {
            Some(mut p2) => {
                let output = p2.finish_with_output();
                if output.status.success() {
                    process.input().write(output.output);
                    process.close_input();
                    process.finish();
                }
                Some(~process)
            }
            None => {
                println("No two remain.");
                Some(~process)
            }}
        }
        None => {
            None
        }}
    }

    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BackgroundProcess::new(cmd) {
        Some(mut process) => {
            match process.run() {
            Some(pid) => {
                println!("{:s} {:i}", name, pid);
                self.processes.push(~process);
            }
            None => {
            }}
        }
        None => {
        }}
    }
}

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

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

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

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

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

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
    Some(ref p) => {
        match p.try_recv() {
        Some (exitstatus) => {
            dead = exitstatus.success();
        },
        None => {
        }}
    }
    _ => {
    }}
    dead
}

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

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

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

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

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

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

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

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


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

//! The variant module contains matchers for asserting properties of enums and convienience functions for Option and Result.

use super::super::*;

/// Matches if the asserted value's variant matches the expected variant.
///
/// # Examples
/// If the enum's variants are already imported one can write:
/// ```rust,ignore
/// assert_that!(Ok(4), is_variant!(Ok));
/// ```rust,ignore
/// If not then the full path of the variant has to be used:
/// ```rust,ignore
/// enum MyEnum { Foo, Bar(i32), Baz{x: i32} }
///
/// assert_that!(MyEnum::Baz{x: 2}, is_variant!(MyEnum::Baz));
/// ```
#[macro_export]
macro_rules! is_variant {
    ( $variant: path ) => {
        Box::new(|actual: &_| {
            use galvanic_assert::MatchResultBuilder;
            let builder = MatchResultBuilder::for_("is_variant");
            match actual {
                &$variant {..} => builder.matched(),
                _ => builder.failed_because(
                        &format!("passed variant does not match '{}'", stringify!($variant))
                )
            }
        })
    }
}

/// Matches the contents of an `Option` againts a passed `Matcher`.
pub fn maybe_some<'a, T: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,Option<T>> + 'a> {
    Box::new(move |maybe_actual: &'a Option<T>| {
        maybe_actual.as_ref()
                    .map_or(MatchResultBuilder::for_("maybe_some")
                                               .failed_because("passed Option is None; cannot evaluate nested matcher"),
                            |actual| matcher.check(actual)
        )
    })
}

/// Matches the contents of a `Result` if it is `Ok` againts a passed `Matcher`.
pub fn maybe_ok<'a, T: 'a, E: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,Result<T,E>> + 'a> {
    Box::new(move |maybe_actual: &'a Result<T,E>| {
        match maybe_actual.as_ref() {
            Ok(actual) => matcher.check(actual),
            Err(_) => MatchResultBuilder::for_("maybe_ok")
                                       .failed_because("passed Result is Err; cannot evaluate nested matcher")
        }
    })
}

/// Matches the contents of a `Result` if it is `Err` againts a passed `Matcher`.
pub fn maybe_err<'a, T: 'a, E: 'a>(matcher: Box<Matcher<'a,E> + 'a>) -> Box<Matcher<'a,Result<T,E>> + 'a> {
    Box::new(move |maybe_actual: &'a Result<T,E>| {
        match maybe_actual.as_ref() {
            Err(actual) => matcher.check(actual),
            Ok(_) => MatchResultBuilder::for_("maybe_err")
                                        .failed_because("passed Result is Ok; cannot evaluate nested matcher")
        }
    })
}
<commit_msg>add examples to docs for variant matchers<commit_after>/* Copyright 2017 Christopher Bacher
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! The variant module contains matchers for asserting properties of enums and convienience functions for Option and Result.

use super::super::*;

/// Matches if the asserted value's variant matches the expected variant.
///
/// # Examples
/// If the enum's variants are already imported one can write:
///
/// ```
/// # #[macro_use] extern crate galvanic_assert;
/// # fn main() {
/// let ok: Result<i32, ()> = Ok(4);
/// assert_that!(&ok, is_variant!(Ok));
/// # }
/// ```
/// If not then the full path of the variant has to be used:
///
/// ```
/// # #[macro_use] extern crate galvanic_assert;
/// # fn main() {
/// enum MyEnum { Foo, Bar(i32), Baz{x: i32} }
/// assert_that!(&MyEnum::Baz{x: 2}, is_variant!(MyEnum::Baz));
/// # }
/// ```
#[macro_export]
macro_rules! is_variant {
    ( $variant: path ) => {
        Box::new(|actual: &_| {
            use galvanic_assert::MatchResultBuilder;
            let builder = MatchResultBuilder::for_("is_variant");
            match actual {
                &$variant {..} => builder.matched(),
                _ => builder.failed_because(
                        &format!("passed variant does not match '{}'", stringify!($variant))
                )
            }
        })
    }
}

/// Matches the contents of an `Option` againts a passed `Matcher`.
///
/// #Examples
/// ```rust
/// # #[macro_use] extern crate galvanic_assert;
/// use galvanic_assert::matchers::*;
/// use galvanic_assert::matchers::variant::*;
/// # fn main() {
/// assert_that!(&Some(32), maybe_some(eq(32)));
/// # }
pub fn maybe_some<'a, T: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,Option<T>> + 'a> {
    Box::new(move |maybe_actual: &'a Option<T>| {
        maybe_actual.as_ref()
                    .map_or(MatchResultBuilder::for_("maybe_some")
                                               .failed_because("passed Option is None; cannot evaluate nested matcher"),
                            |actual| matcher.check(actual)
        )
    })
}

/// Matches the contents of a `Result` if it is `Ok` againts a passed `Matcher`.
///
/// #Examples
/// ```rust
/// # #[macro_use] extern crate galvanic_assert;
/// use galvanic_assert::matchers::*;
/// use galvanic_assert::matchers::variant::*;
/// # fn main() {
/// let ok: Result<i32,()> = Ok(32);
/// assert_that!(&ok, maybe_ok(eq(32)));
/// # }
pub fn maybe_ok<'a, T: 'a, E: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,Result<T,E>> + 'a> {
    Box::new(move |maybe_actual: &'a Result<T,E>| {
        match maybe_actual.as_ref() {
            Ok(actual) => matcher.check(actual),
            Err(_) => MatchResultBuilder::for_("maybe_ok")
                                       .failed_because("passed Result is Err; cannot evaluate nested matcher")
        }
    })
}

/// Matches the contents of a `Result` if it is `Err` againts a passed `Matcher`.
///
/// #Examples
/// ```rust
/// # #[macro_use] extern crate galvanic_assert;
/// use galvanic_assert::matchers::*;
/// use galvanic_assert::matchers::variant::*;
/// # fn main() {
/// let err: Result<i32,i32> = Err(32);
/// assert_that!(&err, maybe_err(eq(32)));
/// # }
pub fn maybe_err<'a, T: 'a, E: 'a>(matcher: Box<Matcher<'a,E> + 'a>) -> Box<Matcher<'a,Result<T,E>> + 'a> {
    Box::new(move |maybe_actual: &'a Result<T,E>| {
        match maybe_actual.as_ref() {
            Err(actual) => matcher.check(actual),
            Ok(_) => MatchResultBuilder::for_("maybe_err")
                                        .failed_because("passed Result is Ok; cannot evaluate nested matcher")
        }
    })
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]

#![feature(convert)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::*;
use glib::Type;
use sysinfo::*;

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(tree_store: &mut gtk::TreeStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    let mut top_level = gtk::TreeIter::new().unwrap();

    tree_store.append(&mut top_level, None);
    tree_store.set_string(&top_level, 0, &format!("{}", pid));
    tree_store.set_string(&top_level, 1, name);
    tree_store.set_string(&top_level, 2, &format!("{}", cpu));
    tree_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn main() {
    gtk::init();

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let mut sys = sysinfo::System::new();

    window.set_title("TreeView Sample");
    window.set_window_position(gtk::WindowPosition::Center);

    gtk::Connect::connect(&window, gtk::signals::DeleteEvent::new(&mut |_| {
        gtk::main_quit();
        true
    }));

    let mut left_tree = gtk::TreeView::new().unwrap();
    let mut scroll = gtk::ScrolledWindow::new(None, None).unwrap();

    scroll.set_min_content_height(800);
    scroll.set_min_content_width(600);

    let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

    append_column("pid", &mut columns);
    append_column("process name", &mut columns);
    append_column("cpu usage", &mut columns);
    append_column("memory usage", &mut columns);

    for i in columns {
        left_tree.append_column(&i);
    }

    let mut tree_store = gtk::TreeStore::new(&[Type::String, Type::String, Type::String, Type::String]).unwrap();
    sys.refresh();
    for (_, pro) in sys.get_processus_list() {
        create_and_fill_model(&mut tree_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }

    left_tree.set_model(&tree_store.get_model().unwrap());
    left_tree.set_headers_visible(true);
    scroll.add(&left_tree);

    /*for _ in 0..10 {
        let mut iter = gtk::TreeIter::new().unwrap();
        left_store.append(&mut iter);
        left_store.set_string(&iter, 0, "I'm in a list");
    }*/

    // display the panes

    //let mut split_pane = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();

    //split_pane.set_size_request(-1, -1);
    //split_pane.add(&left_tree);

    window.add(&scroll);
    window.show_all();
    gtk::main();
}
<commit_msg>Big advancement, however we add an issue while changing data in the model view<commit_after>#![crate_type = "bin"]
#![feature(collections)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{WindowTrait, ContainerTrait, WidgetTrait};
use gtk::signals::DeleteEvent;
use gtk::Connect;
use glib::{Type, timeout};
use sysinfo::*;
use gtk::signal::Inhibit;
use gtk::signal::WidgetSignals;
use std::collections::VecMap;
use std::str::FromStr;

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(tree_store: &mut gtk::TreeStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    let mut top_level = gtk::TreeIter::new().unwrap();

    tree_store.append(&mut top_level, None);
    tree_store.set_string(&top_level, 0, &format!("{}", pid));
    tree_store.set_string(&top_level, 1, name);
    tree_store.set_string(&top_level, 2, &format!("{}", cpu));
    tree_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn update_window(w: &mut (&mut gtk::TreeStore, &mut sysinfo::System)) -> i32 {
    let mut model = w.0.get_model().unwrap();
    let mut iter = gtk::TreeIter::new().unwrap();

    model.get_iter_first(&mut iter);
    w.1.refresh();
    let mut entries : VecMap<Processus> = (*w.1.get_processus_list()).clone();

    loop {
        let pid : i64 = i64::from_str(&model.get_value(&iter, 0).get_string().unwrap()).unwrap();
        let mut to_delete = false;

        match entries.get(&(pid as usize)) {
            Some(p) => {
                w.0.set_string(&iter, 1, &p.name);
                w.0.set_string(&iter, 2, &format!("{}", p.cpu_usage));
                w.0.set_string(&iter, 3, &format!("{}", p.memory));
                to_delete = true;
            }
            None => {
                w.0.remove(&iter);
                model.iter_previous(&mut iter);
            }
        }
        if to_delete {
            entries.remove(&(pid as usize));
        }
        if !model.iter_next(&mut iter) {
            break;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(&mut w.0, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }
    1
}

fn main() {
    gtk::init();

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let mut sys = sysinfo::System::new();

    window.set_title("TreeView Sample");
    window.set_window_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    let mut left_tree = gtk::TreeView::new().unwrap();
    let mut scroll = gtk::ScrolledWindow::new(None, None).unwrap();

    scroll.set_min_content_height(800);
    scroll.set_min_content_width(600);

    let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

    append_column("pid", &mut columns);
    append_column("process name", &mut columns);
    append_column("cpu usage", &mut columns);
    append_column("memory usage", &mut columns);

    for i in columns {
        left_tree.append_column(&i);
    }

    let mut tree_store = gtk::TreeStore::new(&[Type::String, Type::String, Type::String, Type::String]).unwrap();
    sys.refresh();
    for (_, pro) in sys.get_processus_list() {
        create_and_fill_model(&mut tree_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }

    left_tree.set_model(&tree_store.get_model().unwrap());
    left_tree.set_headers_visible(true);
    scroll.add(&left_tree);

    /*for _ in 0..10 {
        let mut iter = gtk::TreeIter::new().unwrap();
        left_store.append(&mut iter);
        left_store.set_string(&iter, 0, "I'm in a list");
    }*/

    // display the panes

    //let mut split_pane = gtk::Box::new(gtk::Orientation::Horizontal, 10).unwrap();

    //split_pane.set_size_request(-1, -1);
    //split_pane.add(&left_tree);

    timeout::add(1500, update_window, &mut (&mut tree_store, &mut sys));

    window.add(&scroll);
    window.show_all();
    gtk::main();
}
<|endoftext|>


<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};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::io::stdin;
use std::option::Option;
use std::io::process;
use std::io::{IoError, io_error};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use extra::getopts;

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

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
        Some(ref p) => {
            match p.try_recv() {
                Some (exitstatus) => {
                    if (exitstatus.success()) {
                        dead = true;
                    }
                },
                _ => {}
            }
        },
        _ => {},
    };
    dead
}

trait Command {
    fn run(&mut self);
}

struct CmdProcess {
    command     : ~str,
    args        : ~[~str],
    exit_status : Option<process::ProcessExit>,
}
impl CmdProcess {
    fn new(command: &str, args: ~[~str]) -> Option<CmdProcess> {
        if (cmd_exists(command)) {
            Some(CmdProcess {
                command: command.to_owned(),
                args: args.to_owned(),
                exit_status: None,
            })
        }
        else { None }
    }
}
impl Command for CmdProcess {
    fn run(&mut self) {
        self.exit_status = run::process_status(self.command, self.args);
    }
}

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_status  : Option<ProcessExit>,
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
}
impl BackgroundProcess {
    fn new(command: &str, args: ~[~str]) -> Option<BackgroundProcess> {
        if (cmd_exists(command)) {
            Some(BackgroundProcess {
                command: command.to_owned(),
                args: args.to_owned(),
                exit_status: None,
                exit_port: None,
                kill_chan: None,
            })
        }
        else { None }
    }
}
impl Command for BackgroundProcess {
    fn run(&mut self) {
        let (port, chan) : (Port<ProcessExit>, Chan<ProcessExit>)
                = Chan::new();
        let (killport, killchan) : (Port<int>, Chan<int>) = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    let signal = killport.recv();
                    let mut error = None;
                    match signal {
                        9 => {
                            io_error::cond.trap(|e: IoError| {
                                error = Some(e);
                                }).inside(|| {
                                    process.force_destroy()
                                })
                        }
                        15 => {
                            io_error::cond.trap(|e: IoError| {
                                error = Some(e);
                                }).inside(|| {
                                    process.force_destroy()
                                })
                        }
                        _ => {}
                    }
                    chan.try_send_deferred(process.finish());
                }
                None => {
                }
            }
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
    }
}

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

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

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
                "" => {}
                _  => { self.push_hist(cmd_line) }
            }
            match program {
                ""        =>  { continue; }
                "exit"    =>  {
                    self.kill_all();
                    return;
                }
                "history" =>  { self.show_hist(); }
                "cd"      =>  { self.chdir(cmd_line); }
                _         =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
                Some(ref kill_channel) => {
                    kill_channel.try_send_deferred(15);
                }
                None => {}
            }

        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

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

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

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != ""
                {
                    Some(x.to_owned())
                }
                else {
                    None
                }).to_owned_vec();
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != ""
                {
                    Some(x.to_owned())
                }
                else {
                    None
                }).to_owned_vec();
        if argv.len() > 0 {
            self.parse_process(argv);
        }
    }

    fn parse_process(&mut self, mut argv : ~[~str]) {
        let program: ~str = argv.remove(0);
        if argv.len() > 0 {
            let last = argv.last().to_owned();
            if last == ~"&" {
                argv.pop();
                self.make_bg_process(program, argv);
            }
            else {
                self.make_fg_process(program, argv);
            }
        }
        else {
            self.make_fg_process(program, argv);
        }
    }

    fn make_fg_process(&mut self, program : ~str, argv : ~[~str]) {
        match CmdProcess::new(program, argv) {
            Some(mut process) => { process.run(); }
            None              => { }
        }
    }

    fn make_bg_process(&mut self, program : ~str, argv : ~[~str]) {
        match BackgroundProcess::new(program, argv) {
            Some(process) => {self.add_process(~process);}
            None          => { }
        }
    }

    fn add_process(&mut self, mut process : ~BackgroundProcess) {
        &process.run();
        self.processes.push(process);
    }
}

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::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>Make fg processes also use process api<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::{run, os};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::option::Option;
use std::io::{stdin, stdio, process, IoError, io_error};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use extra::getopts;

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

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
        Some(ref p) => {
            match p.try_recv() {
                Some (exitstatus) => {
                    if (exitstatus.success()) {
                        dead = true;
                    }
                },
                _ => {}
            }
        },
        _ => {},
    };
    dead
}

trait Command {
    fn run(&mut self) -> Option<Process>;
}

struct CmdProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : i32,
    stdout      : i32,
    exit_status : Option<process::ProcessExit>,
}
impl CmdProcess {
    fn new(command: &str, args: ~[~str], stdin : i32, stdout : i32) -> Option<CmdProcess> {
        if (cmd_exists(command)) {
            Some(CmdProcess {
                command     : command.to_owned(),
                args        : args.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
                exit_status : None,
            })
        }
        else { None }
    }
}
impl Command for CmdProcess {
    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : Some(self.stdin),
            out_fd : Some(self.stdout),
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_status  : Option<ProcessExit>,
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
}
impl BackgroundProcess {
    fn new(command: &str, args: ~[~str]) -> Option<BackgroundProcess> {
        if (cmd_exists(command)) {
            Some(BackgroundProcess {
                command: command.to_owned(),
                args: args.to_owned(),
                exit_status: None,
                exit_port: None,
                kill_chan: None,
            })
        }
        else { None }
    }
    fn run(&mut self) {
        let (port, chan) : (Port<ProcessExit>, Chan<ProcessExit>)
                = Chan::new();
        let (killport, killchan) : (Port<int>, Chan<int>) = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
                Some(mut process) => {
                    let signal = killport.recv();
                    let mut error = None;
                    match signal {
                        9 => {
                            io_error::cond.trap(|e: IoError| {
                                error = Some(e);
                                }).inside(|| {
                                    process.force_destroy()
                                })
                        }
                        15 => {
                            io_error::cond.trap(|e: IoError| {
                                error = Some(e);
                                }).inside(|| {
                                    process.force_destroy()
                                })
                        }
                        _ => {}
                    }
                    chan.try_send_deferred(process.finish());
                }
                None => {
                }
            }
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
    }
}

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

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

        loop {
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
                "" => {}
                _  => { self.push_hist(cmd_line) }
            }
            match program {
                ""        =>  { continue; }
                "exit"    =>  {
                    self.kill_all();
                    return;
                }
                "history" =>  { self.show_hist(); }
                "cd"      =>  { self.chdir(cmd_line); }
                _         =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
                Some(ref kill_channel) => {
                    kill_channel.try_send_deferred(15);
                }
                None => {}
            }

        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

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

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

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != ""
                {
                    Some(x.to_owned())
                }
                else {
                    None
                }).to_owned_vec();
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != ""
                {
                    Some(x.to_owned())
                }
                else {
                    None
                }).to_owned_vec();
        if argv.len() > 0 {
            self.parse_process(argv);
        }
    }

    fn parse_process(&mut self, mut argv : ~[~str]) {
        let program: ~str = argv.remove(0);
        if argv.len() > 0 {
            let last = argv.last().to_owned();
            if last == ~"&" {
                argv.pop();
                self.make_bg_process(program, argv);
            }
            else {
                self.make_fg_process(program, argv);
            }
        }
        else {
            self.make_fg_process(program, argv);
        }
    }

    fn make_fg_process(&mut self, program : ~str, argv : ~[~str]) {
        match CmdProcess::new(program, argv, 0, 1) {
            Some(mut cmdprocess) => {
                cmdprocess.run();
            }
            None              => { }
        }
    }

    fn make_bg_process(&mut self, program : ~str, argv : ~[~str]) {
        match BackgroundProcess::new(program, argv) {
            Some(process) => {self.add_process(~process);}
            None          => { }
        }
    }

    fn add_process(&mut self, mut process : ~BackgroundProcess) {
        &process.run();
        self.processes.push(process);
    }
}

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::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<|endoftext|>


<commit_before>//! Extended precision "soft float", for internal use only.

// This module is only for dec2flt and flt2dec, and only public because of coretests.
// It is not intended to ever be stabilized.
#![doc(hidden)]
#![unstable(
    feature = "core_private_diy_float",
    reason = "internal routines only exposed for testing",
    issue = "none"
)]

/// A custom 64-bit floating point type, representing `f * 2^e`.
#[derive(Copy, Clone, Debug)]
#[doc(hidden)]
pub struct Fp {
    /// The integer mantissa.
    pub f: u64,
    /// The exponent in base 2.
    pub e: i16,
}

impl Fp {
    /// Returns a correctly rounded product of itself and `other`.
    pub fn mul(&self, other: &Fp) -> Fp {
        const MASK: u64 = 0xffffffff;
        let a = self.f >> 32;
        let b = self.f & MASK;
        let c = other.f >> 32;
        let d = other.f & MASK;
        let ac = a * c;
        let bc = b * c;
        let ad = a * d;
        let bd = b * d;
        let tmp = (bd >> 32) + (ad & MASK) + (bc & MASK) + (1 << 31) /* round */;
        let f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
        let e = self.e + other.e + 64;
        Fp { f, e }
    }

    /// Normalizes itself so that the resulting mantissa is at least `2^63`.
    pub fn normalize(&self) -> Fp {
        let mut f = self.f;
        let mut e = self.e;
        if f >> (64 - 32) == 0 {
            f <<= 32;
            e -= 32;
        }
        if f >> (64 - 16) == 0 {
            f <<= 16;
            e -= 16;
        }
        if f >> (64 - 8) == 0 {
            f <<= 8;
            e -= 8;
        }
        if f >> (64 - 4) == 0 {
            f <<= 4;
            e -= 4;
        }
        if f >> (64 - 2) == 0 {
            f <<= 2;
            e -= 2;
        }
        if f >> (64 - 1) == 0 {
            f <<= 1;
            e -= 1;
        }
        debug_assert!(f >= (1 >> 63));
        Fp { f, e }
    }

    /// Normalizes itself to have the shared exponent.
    /// It can only decrease the exponent (and thus increase the mantissa).
    pub fn normalize_to(&self, e: i16) -> Fp {
        let edelta = self.e - e;
        assert!(edelta >= 0);
        let edelta = edelta as usize;
        assert_eq!(self.f << edelta >> edelta, self.f);
        Fp { f: self.f << edelta, e }
    }
}
<commit_msg>Fix assert in diy_float<commit_after>//! Extended precision "soft float", for internal use only.

// This module is only for dec2flt and flt2dec, and only public because of coretests.
// It is not intended to ever be stabilized.
#![doc(hidden)]
#![unstable(
    feature = "core_private_diy_float",
    reason = "internal routines only exposed for testing",
    issue = "none"
)]

/// A custom 64-bit floating point type, representing `f * 2^e`.
#[derive(Copy, Clone, Debug)]
#[doc(hidden)]
pub struct Fp {
    /// The integer mantissa.
    pub f: u64,
    /// The exponent in base 2.
    pub e: i16,
}

impl Fp {
    /// Returns a correctly rounded product of itself and `other`.
    pub fn mul(&self, other: &Fp) -> Fp {
        const MASK: u64 = 0xffffffff;
        let a = self.f >> 32;
        let b = self.f & MASK;
        let c = other.f >> 32;
        let d = other.f & MASK;
        let ac = a * c;
        let bc = b * c;
        let ad = a * d;
        let bd = b * d;
        let tmp = (bd >> 32) + (ad & MASK) + (bc & MASK) + (1 << 31) /* round */;
        let f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
        let e = self.e + other.e + 64;
        Fp { f, e }
    }

    /// Normalizes itself so that the resulting mantissa is at least `2^63`.
    pub fn normalize(&self) -> Fp {
        let mut f = self.f;
        let mut e = self.e;
        if f >> (64 - 32) == 0 {
            f <<= 32;
            e -= 32;
        }
        if f >> (64 - 16) == 0 {
            f <<= 16;
            e -= 16;
        }
        if f >> (64 - 8) == 0 {
            f <<= 8;
            e -= 8;
        }
        if f >> (64 - 4) == 0 {
            f <<= 4;
            e -= 4;
        }
        if f >> (64 - 2) == 0 {
            f <<= 2;
            e -= 2;
        }
        if f >> (64 - 1) == 0 {
            f <<= 1;
            e -= 1;
        }
        debug_assert!(f >= (1 << 63));
        Fp { f, e }
    }

    /// Normalizes itself to have the shared exponent.
    /// It can only decrease the exponent (and thus increase the mantissa).
    pub fn normalize_to(&self, e: i16) -> Fp {
        let edelta = self.e - e;
        assert!(edelta >= 0);
        let edelta = edelta as usize;
        assert_eq!(self.f << edelta >> edelta, self.f);
        Fp { f: self.f << edelta, e }
    }
}
<|endoftext|>


<commit_before>use std::ffi;
use std::marker;
use std::mem;
use std::slice;
use std::str;
use libc::{c_void, c_int, c_char, c_uint};

use {raw, panic, Error, Cred, CredentialType, Oid};
use util::Binding;

/// A structure to contain the callbacks which are invoked when a repository is
/// being updated or downloaded.
///
/// These callbacks are used to manage facilities such as authentication,
/// transfer progress, etc.
pub struct RemoteCallbacks<'a> {
    progress: Option<Box<TransferProgress<'a>>>,
    credentials: Option<Box<Credentials<'a>>>,
    sideband_progress: Option<Box<TransportMessage<'a>>>,
    update_tips: Option<Box<UpdateTips<'a>>>,
}

/// Struct representing the progress by an in-flight transfer.
pub struct Progress<'a> {
    raw: ProgressState,
    marker: marker::ContravariantLifetime<'a>,
}

enum ProgressState {
    Borrowed(*const raw::git_transfer_progress),
    Owned(raw::git_transfer_progress),
}

/// Callback used to acquire credentials for when a remote is fetched.
///
/// * `url` - the resource for which the credentials are required.
/// * `username_from_url` - the username that was embedded in the url, or `None`
///                         if it was not included.
/// * `allowed_types` - a bitmask stating which cred types are ok to return.
// TODO: the second &str should be Option<&str> but that currently ICEs
pub type Credentials<'a> = FnMut(&str, &str, CredentialType)
                                 -> Result<Cred, Error> + 'a;

/// Callback to be invoked while a transfer is in progress.
///
/// This callback will be periodically called with updates to the progress of
/// the transfer so far. The return value indicates whether the transfer should
/// continue. A return value of `false` will cancel the transfer.
///
/// * `progress` - the progress being made so far.
pub type TransferProgress<'a> = FnMut(Progress) -> bool + 'a;

/// Callback for receiving messages delivered by the transport.
///
/// The return value indicates whether the network operation should continue.
pub type TransportMessage<'a> = FnMut(&[u8]) -> bool + 'a;

/// Callback for whenever a reference is updated locally.
pub type UpdateTips<'a> = FnMut(&str, Oid, Oid) -> bool + 'a;

impl<'a> RemoteCallbacks<'a> {
    /// Creates a new set of empty callbacks
    pub fn new() -> RemoteCallbacks<'a> {
        RemoteCallbacks {
            credentials: None,
            progress: None,
            sideband_progress: None,
            update_tips: None,
        }
    }

    /// The callback through which to fetch credentials if required.
    pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                          where F: FnMut(&str, &str, CredentialType)
                                         -> Result<Cred, Error> + 'a
    {
        self.credentials = Some(Box::new(cb) as Box<Credentials<'a>>);
        self
    }

    /// The callback through which progress is monitored.
    pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                                where F: FnMut(Progress) -> bool + 'a {
        self.progress = Some(Box::new(cb) as Box<TransferProgress<'a>>);
        self
    }

    /// Textual progress from the remote.
    ///
    /// Text sent over the progress side-band will be passed to this function
    /// (this is the 'counting objects' output.
    pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                                where F: FnMut(&[u8]) -> bool + 'a {
        self.sideband_progress = Some(Box::new(cb) as Box<TransportMessage<'a>>);
        self
    }

    /// Each time a reference is updated locally, the callback will be called
    /// with information about it.
    pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                          where F: FnMut(&str, Oid, Oid) -> bool + 'a {
        self.update_tips = Some(Box::new(cb) as Box<UpdateTips<'a>>);
        self
    }
}

impl<'a> Binding for RemoteCallbacks<'a> {
    type Raw = raw::git_remote_callbacks;
    unsafe fn from_raw(_raw: raw::git_remote_callbacks) -> RemoteCallbacks<'a> {
        panic!("unimplemented");
    }

    fn raw(&self) -> raw::git_remote_callbacks {
        unsafe {
            let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
            assert_eq!(raw::git_remote_init_callbacks(&mut callbacks,
                                        raw::GIT_REMOTE_CALLBACKS_VERSION), 0);
            if self.progress.is_some() {
                let f: raw::git_transfer_progress_cb = transfer_progress_cb;
                callbacks.transfer_progress = Some(f);
            }
            if self.credentials.is_some() {
                let f: raw::git_cred_acquire_cb = credentials_cb;
                callbacks.credentials = Some(f);
            }
            if self.sideband_progress.is_some() {
                let f: raw::git_transport_message_cb = sideband_progress_cb;
                callbacks.sideband_progress = Some(f);
            }
            if self.update_tips.is_some() {
                let f: extern fn(*const c_char, *const raw::git_oid,
                                 *const raw::git_oid, *mut c_void) -> c_int
                                = update_tips_cb;
                callbacks.update_tips = Some(f);
            }
            callbacks.payload = self as *const _ as *mut _;
            return callbacks;
        }
    }
}

impl<'a> Progress<'a> {
    /// Number of objects in the packfile being downloaded
    pub fn total_objects(&self) -> usize {
        unsafe { (*self.raw()).total_objects as usize }
    }
    /// Received objects that have been hashed
    pub fn indexed_objects(&self) -> usize {
        unsafe { (*self.raw()).indexed_objects as usize }
    }
    /// Objects which have been downloaded
    pub fn received_objects(&self) -> usize {
        unsafe { (*self.raw()).received_objects as usize }
    }
    /// Locally-available objects that have been injected in order to fix a thin
    /// pack.
    pub fn local_objects(&self) -> usize {
        unsafe { (*self.raw()).local_objects as usize }
    }
    /// Number of deltas in the packfile being downloaded
    pub fn total_deltas(&self) -> usize {
        unsafe { (*self.raw()).total_deltas as usize }
    }
    /// Received deltas that have been hashed.
    pub fn indexed_deltas(&self) -> usize {
        unsafe { (*self.raw()).indexed_deltas as usize }
    }
    /// Size of the packfile received up to now
    pub fn received_bytes(&self) -> usize {
        unsafe { (*self.raw()).received_bytes as usize }
    }

    /// Convert this to an owned version of `Progress`.
    pub fn to_owned(&self) -> Progress<'static> {
        Progress {
            raw: ProgressState::Owned(unsafe { *self.raw() }),
            marker: marker::ContravariantLifetime,
        }
    }
}

impl<'a> Binding for Progress<'a> {
    type Raw = *const raw::git_transfer_progress;
    unsafe fn from_raw(raw: *const raw::git_transfer_progress)
                           -> Progress<'a> {
        Progress {
            raw: ProgressState::Borrowed(raw),
            marker: marker::ContravariantLifetime,
        }
    }

    fn raw(&self) -> *const raw::git_transfer_progress {
        match self.raw {
            ProgressState::Borrowed(raw) => raw,
            ProgressState::Owned(ref raw) => raw as *const _,
        }
    }
}

extern fn credentials_cb(ret: *mut *mut raw::git_cred,
                         url: *const c_char,
                         username_from_url: *const c_char,
                         allowed_types: c_uint,
                         payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.credentials {
            Some(ref mut c) => c,
            None => return raw::GIT_PASSTHROUGH as c_int,
        };
        *ret = 0 as *mut raw::git_cred;
        let url = match str::from_utf8(ffi::c_str_to_bytes(&url))  {
            Ok(url) => url,
            Err(_) => return raw::GIT_PASSTHROUGH as c_int,
        };
        let username_from_url = if username_from_url.is_null() {
            None
        } else {
            Some(ffi::c_str_to_bytes(&username_from_url))
        };
        let username_from_url = match username_from_url {
            Some(username) => match str::from_utf8(username) {
                Ok(s) => Some(s),
                Err(_) => return raw::GIT_PASSTHROUGH as c_int,
            },
            None => None,
        };
        let username_from_url = username_from_url.unwrap_or("");

        let cred_type = CredentialType::from_bits_truncate(allowed_types as u32);
        match panic::wrap(|| {
            callback(url, username_from_url, cred_type)
        }) {
            Some(Ok(cred)) => {
                // Turns out it's a memory safety issue if we pass through any
                // and all credentials into libgit2
                if allowed_types & (cred.credtype() as c_uint) != 0 {
                    *ret = cred.unwrap();
                    0
                } else {
                    raw::GIT_PASSTHROUGH as c_int
                }
            }
            Some(Err(e)) => e.raw_code() as c_int,
            None => -1,
        }
    }
}

extern fn transfer_progress_cb(stats: *const raw::git_transfer_progress,
                               payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.progress {
            Some(ref mut c) => c,
            None => return 0,
        };
        let progress = Binding::from_raw(stats);
        let ok = panic::wrap(move || {
            callback(progress)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}

extern fn sideband_progress_cb(str: *const c_char,
                               len: c_int,
                               payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.sideband_progress {
            Some(ref mut c) => c,
            None => return 0,
        };
        let ptr = str as *const u8;
        let buf = slice::from_raw_buf(&ptr, len as usize);
        let ok = panic::wrap(|| {
            callback(buf)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}

extern fn update_tips_cb(refname: *const c_char,
                         a: *const raw::git_oid,
                         b: *const raw::git_oid,
                         data: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(data as *mut RemoteCallbacks);
        let callback = match payload.update_tips {
            Some(ref mut c) => c,
            None => return 0,
        };
        let refname = str::from_utf8(ffi::c_str_to_bytes(&refname)).ok().unwrap();
        let a = Binding::from_raw(a);
        let b = Binding::from_raw(b);
        let ok = panic::wrap(|| {
            callback(refname, a, b)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}
<commit_msg>Fix a previously ICE-ing type definition<commit_after>use std::ffi;
use std::marker;
use std::mem;
use std::slice;
use std::str;
use libc::{c_void, c_int, c_char, c_uint};

use {raw, panic, Error, Cred, CredentialType, Oid};
use util::Binding;

/// A structure to contain the callbacks which are invoked when a repository is
/// being updated or downloaded.
///
/// These callbacks are used to manage facilities such as authentication,
/// transfer progress, etc.
pub struct RemoteCallbacks<'a> {
    progress: Option<Box<TransferProgress<'a>>>,
    credentials: Option<Box<Credentials<'a>>>,
    sideband_progress: Option<Box<TransportMessage<'a>>>,
    update_tips: Option<Box<UpdateTips<'a>>>,
}

/// Struct representing the progress by an in-flight transfer.
pub struct Progress<'a> {
    raw: ProgressState,
    marker: marker::ContravariantLifetime<'a>,
}

enum ProgressState {
    Borrowed(*const raw::git_transfer_progress),
    Owned(raw::git_transfer_progress),
}

/// Callback used to acquire credentials for when a remote is fetched.
///
/// * `url` - the resource for which the credentials are required.
/// * `username_from_url` - the username that was embedded in the url, or `None`
///                         if it was not included.
/// * `allowed_types` - a bitmask stating which cred types are ok to return.
pub type Credentials<'a> = FnMut(&str, Option<&str>, CredentialType)
                                 -> Result<Cred, Error> + 'a;

/// Callback to be invoked while a transfer is in progress.
///
/// This callback will be periodically called with updates to the progress of
/// the transfer so far. The return value indicates whether the transfer should
/// continue. A return value of `false` will cancel the transfer.
///
/// * `progress` - the progress being made so far.
pub type TransferProgress<'a> = FnMut(Progress) -> bool + 'a;

/// Callback for receiving messages delivered by the transport.
///
/// The return value indicates whether the network operation should continue.
pub type TransportMessage<'a> = FnMut(&[u8]) -> bool + 'a;

/// Callback for whenever a reference is updated locally.
pub type UpdateTips<'a> = FnMut(&str, Oid, Oid) -> bool + 'a;

impl<'a> RemoteCallbacks<'a> {
    /// Creates a new set of empty callbacks
    pub fn new() -> RemoteCallbacks<'a> {
        RemoteCallbacks {
            credentials: None,
            progress: None,
            sideband_progress: None,
            update_tips: None,
        }
    }

    /// The callback through which to fetch credentials if required.
    pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                          where F: FnMut(&str, Option<&str>, CredentialType)
                                         -> Result<Cred, Error> + 'a
    {
        self.credentials = Some(Box::new(cb) as Box<Credentials<'a>>);
        self
    }

    /// The callback through which progress is monitored.
    pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                                where F: FnMut(Progress) -> bool + 'a {
        self.progress = Some(Box::new(cb) as Box<TransferProgress<'a>>);
        self
    }

    /// Textual progress from the remote.
    ///
    /// Text sent over the progress side-band will be passed to this function
    /// (this is the 'counting objects' output.
    pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                                where F: FnMut(&[u8]) -> bool + 'a {
        self.sideband_progress = Some(Box::new(cb) as Box<TransportMessage<'a>>);
        self
    }

    /// Each time a reference is updated locally, the callback will be called
    /// with information about it.
    pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
                          where F: FnMut(&str, Oid, Oid) -> bool + 'a {
        self.update_tips = Some(Box::new(cb) as Box<UpdateTips<'a>>);
        self
    }
}

impl<'a> Binding for RemoteCallbacks<'a> {
    type Raw = raw::git_remote_callbacks;
    unsafe fn from_raw(_raw: raw::git_remote_callbacks) -> RemoteCallbacks<'a> {
        panic!("unimplemented");
    }

    fn raw(&self) -> raw::git_remote_callbacks {
        unsafe {
            let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
            assert_eq!(raw::git_remote_init_callbacks(&mut callbacks,
                                        raw::GIT_REMOTE_CALLBACKS_VERSION), 0);
            if self.progress.is_some() {
                let f: raw::git_transfer_progress_cb = transfer_progress_cb;
                callbacks.transfer_progress = Some(f);
            }
            if self.credentials.is_some() {
                let f: raw::git_cred_acquire_cb = credentials_cb;
                callbacks.credentials = Some(f);
            }
            if self.sideband_progress.is_some() {
                let f: raw::git_transport_message_cb = sideband_progress_cb;
                callbacks.sideband_progress = Some(f);
            }
            if self.update_tips.is_some() {
                let f: extern fn(*const c_char, *const raw::git_oid,
                                 *const raw::git_oid, *mut c_void) -> c_int
                                = update_tips_cb;
                callbacks.update_tips = Some(f);
            }
            callbacks.payload = self as *const _ as *mut _;
            return callbacks;
        }
    }
}

impl<'a> Progress<'a> {
    /// Number of objects in the packfile being downloaded
    pub fn total_objects(&self) -> usize {
        unsafe { (*self.raw()).total_objects as usize }
    }
    /// Received objects that have been hashed
    pub fn indexed_objects(&self) -> usize {
        unsafe { (*self.raw()).indexed_objects as usize }
    }
    /// Objects which have been downloaded
    pub fn received_objects(&self) -> usize {
        unsafe { (*self.raw()).received_objects as usize }
    }
    /// Locally-available objects that have been injected in order to fix a thin
    /// pack.
    pub fn local_objects(&self) -> usize {
        unsafe { (*self.raw()).local_objects as usize }
    }
    /// Number of deltas in the packfile being downloaded
    pub fn total_deltas(&self) -> usize {
        unsafe { (*self.raw()).total_deltas as usize }
    }
    /// Received deltas that have been hashed.
    pub fn indexed_deltas(&self) -> usize {
        unsafe { (*self.raw()).indexed_deltas as usize }
    }
    /// Size of the packfile received up to now
    pub fn received_bytes(&self) -> usize {
        unsafe { (*self.raw()).received_bytes as usize }
    }

    /// Convert this to an owned version of `Progress`.
    pub fn to_owned(&self) -> Progress<'static> {
        Progress {
            raw: ProgressState::Owned(unsafe { *self.raw() }),
            marker: marker::ContravariantLifetime,
        }
    }
}

impl<'a> Binding for Progress<'a> {
    type Raw = *const raw::git_transfer_progress;
    unsafe fn from_raw(raw: *const raw::git_transfer_progress)
                           -> Progress<'a> {
        Progress {
            raw: ProgressState::Borrowed(raw),
            marker: marker::ContravariantLifetime,
        }
    }

    fn raw(&self) -> *const raw::git_transfer_progress {
        match self.raw {
            ProgressState::Borrowed(raw) => raw,
            ProgressState::Owned(ref raw) => raw as *const _,
        }
    }
}

extern fn credentials_cb(ret: *mut *mut raw::git_cred,
                         url: *const c_char,
                         username_from_url: *const c_char,
                         allowed_types: c_uint,
                         payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.credentials {
            Some(ref mut c) => c,
            None => return raw::GIT_PASSTHROUGH as c_int,
        };
        *ret = 0 as *mut raw::git_cred;
        let url = match str::from_utf8(ffi::c_str_to_bytes(&url))  {
            Ok(url) => url,
            Err(_) => return raw::GIT_PASSTHROUGH as c_int,
        };
        let username_from_url = if username_from_url.is_null() {
            None
        } else {
            Some(ffi::c_str_to_bytes(&username_from_url))
        };
        let username_from_url = match username_from_url {
            Some(username) => match str::from_utf8(username) {
                Ok(s) => Some(s),
                Err(_) => return raw::GIT_PASSTHROUGH as c_int,
            },
            None => None,
        };

        let cred_type = CredentialType::from_bits_truncate(allowed_types as u32);
        match panic::wrap(|| {
            callback(url, username_from_url, cred_type)
        }) {
            Some(Ok(cred)) => {
                // Turns out it's a memory safety issue if we pass through any
                // and all credentials into libgit2
                if allowed_types & (cred.credtype() as c_uint) != 0 {
                    *ret = cred.unwrap();
                    0
                } else {
                    raw::GIT_PASSTHROUGH as c_int
                }
            }
            Some(Err(e)) => e.raw_code() as c_int,
            None => -1,
        }
    }
}

extern fn transfer_progress_cb(stats: *const raw::git_transfer_progress,
                               payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.progress {
            Some(ref mut c) => c,
            None => return 0,
        };
        let progress = Binding::from_raw(stats);
        let ok = panic::wrap(move || {
            callback(progress)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}

extern fn sideband_progress_cb(str: *const c_char,
                               len: c_int,
                               payload: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
        let callback = match payload.sideband_progress {
            Some(ref mut c) => c,
            None => return 0,
        };
        let ptr = str as *const u8;
        let buf = slice::from_raw_buf(&ptr, len as usize);
        let ok = panic::wrap(|| {
            callback(buf)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}

extern fn update_tips_cb(refname: *const c_char,
                         a: *const raw::git_oid,
                         b: *const raw::git_oid,
                         data: *mut c_void) -> c_int {
    unsafe {
        let payload: &mut RemoteCallbacks = &mut *(data as *mut RemoteCallbacks);
        let callback = match payload.update_tips {
            Some(ref mut c) => c,
            None => return 0,
        };
        let refname = str::from_utf8(ffi::c_str_to_bytes(&refname)).ok().unwrap();
        let a = Binding::from_raw(a);
        let b = Binding::from_raw(b);
        let ok = panic::wrap(|| {
            callback(refname, a, b)
        }).unwrap_or(false);
        if ok {0} else {-1}
    }
}
<|endoftext|>