<commit_before>use syntax::ast::expr::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, VarDeclExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, UnaryOpExpr, BinOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use syntax::ast::constant::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use syntax::ast::op::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use syntax::ast::op::{UnaryMinus, UnaryPlus, UnaryNot};
use syntax::ast::op::{BinNum, BinBit, BinLog, BinComp};
use syntax::ast::op::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use syntax::ast::op::{LogAnd, LogOr};
use syntax::ast::op::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use stdlib::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value, ToValue};
use stdlib::object::{INSTANCE_PROTOTYPE, PROTOTYPE};
use stdlib::function::{NativeFunc, RegularFunc, RegularFunction};
use stdlib::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use jit::{Context, Function, Type, Types, CDECL};
use jit;
/// A variable scope
pub struct Scope {
	/// The value of `this` in the scope
	pub this: Value,
	/// The variables declared in the scope
	pub vars: Value
}
/// An execution engine
pub trait Executor<T> {
	/// Make a new execution engine
	fn new() -> Self;
	/// Set a global variable called `name` with the value `val`
	fn set_global(&mut self, name:String, val:Value) -> Value;
	/// Resolve the global variable `name`
	fn get_global(&self, name:String) -> Value;
	/// Create a new scope and return it
	fn make_scope(&mut self, this:Value) -> Scope;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> Scope;
	/// Compile the expression
	fn compile(&self, expr:&Expr) -> Box<T>;
	/// Run an expression
	fn run(&mut self, comp:Box<T>) -> ResultValue;
}
/// A Javascript JIT compiler
pub struct JITCompiler {
	/// The JIT Context
	context: Box<Context>,
	/// An object representing the global object
	pub global: Value,
	/// The scopes
	pub scopes: Vec<Scope>
}
impl JITCompiler {
	#[inline(always)]
	/// Get the current scope
	pub fn scope(&self) -> Scope {
		*self.scopes.get(self.scopes.len() - 1)
	}
	fn with_builder<R>(&self, cb: || -> R) -> R {
		self.context.build_start();
		let rv = cb();
		self.context.build_end();
		rv
	}
}
impl Executor<Function> for JITCompiler {
	fn new() -> JITCompiler {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		string::init(global);
		uri::init(global);
		JITCompiler {
			context: Context::new(),
			global: global,
			scopes: vec!(Scope {
				this: global,
				vars: global
			})
		}
	}
	#[inline(always)]
	fn set_global(&mut self, name:String, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	#[inline(always)]
	fn get_global(&self, name:String) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self, this:Value) -> Scope {
		let scope = Scope {
			this: this,
			vars: ValueData::new_obj(None)
		};
		self.scopes.push(scope);
		scope
	}
	#[inline(always)]
	fn destroy_scope(&mut self) -> Scope {
		self.scopes.pop().unwrap()
	}
	fn compile(&self, expr: &Expr) -> Box<Function> {
		self.with_builder(|| {
			let valuedata_t = Types::get_int();
			let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
			let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
			let default_sig_t = Type::create_signature(CDECL, &*value_t, &[]);
			fn compile_value(func:&Function, expr: &Expr) -> Box<jit::Value> {
				let valuedata_t = Types::get_int();
				let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
				let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
				match expr.def {
					ConstExpr(CNull) => {
						fn create_null_value() -> Value {
							Gc::new(VNull)
						}
						let create_null_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_null_value", create_null_value, &*create_null_sig, &[])
					},
					ConstExpr(CUndefined) => {
						fn create_undef_value() -> Value {
							Gc::new(VUndefined)
						}
						let create_undef_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_undef_value", create_undef_value, &*create_undef_sig, &[])
					},
					ConstExpr(CBool(v)) => {
						let create_bool_value = to_value::<bool>;
						let val = func.constant_int32_as_type(v as i32, &*Types::get_bool());
						let create_bool_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_bool()]);
						func.insn_call_native1("create_bool_value", create_bool_value, &*create_bool_sig, &[&*val])
					},
					ConstExpr(CNum(n)) => {
						let create_number_value = to_value::<f64>;
						let val = func.constant_float64(n);
						let create_number_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_float64()]);
						func.insn_call_native1("create_number_value", create_number_value, &* create_number_sig, &[&*val])
					},
					UnaryOpExpr(op, ref a) => {
						let i_a = compile_value(func, *a);
						let unop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t]);
						let (name, op_func) = match op {
							UnaryPlus => return i_a,
							UnaryMinus => {
								fn neg_value(a:Value) -> Value {
									Gc::new(-a.borrow())
								}
								("neg", neg_value)
							}
							UnaryNot => {
								fn not_value(a: Value) -> Value {
									Gc::new(!a.borrow())
								}
								("not", not_value)
							},
							_ => fail!("Unimplemented {}", op)
						};
						func.insn_call_native1(name, op_func, unop_sig, &[&*i_a])
					},
					BinOpExpr(op, ref a, ref b) => {
						let i_a = compile_value(func, *a);
						let i_b = compile_value(func, *b);
						let binop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t, &*value_t]);

						let (name, op_func) = match op {
							BinNum(OpAdd) => {
								fn add_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() + *b.borrow())
								}
								("add", add_values)
							},
							BinNum(OpSub) => {
								fn sub_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() - *b.borrow())
								}
								("sub", sub_values)
							},
							BinNum(OpMul) => {
								fn mul_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() * *b.borrow())
								}
								("mul", mul_values)
							},
							BinNum(OpDiv) => {
								fn div_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() / *b.borrow())
								}
								("div", div_values)
							},
							BinNum(OpMod) => {
								fn mod_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() % *b.borrow())
								}
								("mod", mod_values)
							},
							BinBit(BitAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() & *b.borrow())
								}
								("and", and_values)
							},
							BinBit(BitOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() | *b.borrow())
								}
								("or", or_values)
							},
							BinBit(BitXor) => {
								fn xor_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() ^ *b.borrow())
								}
								("xor", xor_values)
							},
							BinBit(BitShl) => {
								fn shl_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() << *b.borrow())
								}
								("shl", shl_values)
							},
							BinBit(BitShr) => {
								fn shr_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() >> *b.borrow())
								}
								("shr", shr_values)
							},
							BinLog(LogOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() || b.borrow().is_true())
								}
								("or", or_values)
							},
							BinLog(LogAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() && b.borrow().is_true())
								}
								("and", and_values)
							},
							BinComp(CompEqual) | BinComp(CompStrictEqual) => {
								fn eq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() == b.borrow())
								}
								("eq", eq_values)
							},
							BinComp(CompNotEqual) | BinComp(CompStrictNotEqual) => {
								fn neq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() != b.borrow())
								}
								("neq", neq_values)
							},
							BinComp(CompLessThan) => {
								fn lt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() < b.borrow())
								}
								("lt", lt_values)
							},
							BinComp(CompLessThanOrEqual) => {
								fn lte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() <= b.borrow())
								}
								("lte", lte_values)
							},
							BinComp(CompGreaterThan) => {
								fn gt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() > b.borrow())
								}
								("gt", gt_values)
							},
							BinComp(CompGreaterThanOrEqual) => {
								fn gte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() >= b.borrow())
								}
								("gte", gte_values)
							}
						};
						func.insn_call_native2(name, op_func, binop_sig, &[&*i_a, &*i_b])
					},
					_ => fail!("Unimplemented {}", expr)
				}
			}
			let func = self.context.create_function(&*default_sig_t);
			let value = compile_value(func, expr);
			func.insn_return(&*value);
			func.compile();
			func
		})
	}
	fn run(&mut self, comp:Box<Function>) -> ResultValue {
		let func: fn() -> Value = comp.closure();
		return Ok(func());
	}
}<commit_msg>Implement JIT for return expression<commit_after>use syntax::ast::expr::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, VarDeclExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, UnaryOpExpr, BinOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use syntax::ast::constant::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use syntax::ast::op::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use syntax::ast::op::{UnaryMinus, UnaryPlus, UnaryNot};
use syntax::ast::op::{BinNum, BinBit, BinLog, BinComp};
use syntax::ast::op::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use syntax::ast::op::{LogAnd, LogOr};
use syntax::ast::op::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use stdlib::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value, ToValue};
use stdlib::object::{INSTANCE_PROTOTYPE, PROTOTYPE};
use stdlib::function::{NativeFunc, RegularFunc, RegularFunction};
use stdlib::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use jit::{Context, Function, Type, Types, CDECL};
use jit;
/// A variable scope
pub struct Scope {
	/// The value of `this` in the scope
	pub this: Value,
	/// The variables declared in the scope
	pub vars: Value
}
/// An execution engine
pub trait Executor<T> {
	/// Make a new execution engine
	fn new() -> Self;
	/// Set a global variable called `name` with the value `val`
	fn set_global(&mut self, name:String, val:Value) -> Value;
	/// Resolve the global variable `name`
	fn get_global(&self, name:String) -> Value;
	/// Create a new scope and return it
	fn make_scope(&mut self, this:Value) -> Scope;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> Scope;
	/// Compile the expression
	fn compile(&self, expr:&Expr) -> Box<T>;
	/// Run an expression
	fn run(&mut self, comp:Box<T>) -> ResultValue;
}
/// A Javascript JIT compiler
pub struct JITCompiler {
	/// The JIT Context
	context: Box<Context>,
	/// An object representing the global object
	pub global: Value,
	/// The scopes
	pub scopes: Vec<Scope>
}
impl JITCompiler {
	#[inline(always)]
	/// Get the current scope
	pub fn scope(&self) -> Scope {
		*self.scopes.get(self.scopes.len() - 1)
	}
	fn with_builder<R>(&self, cb: || -> R) -> R {
		self.context.build_start();
		let rv = cb();
		self.context.build_end();
		rv
	}
}
impl Executor<Function> for JITCompiler {
	fn new() -> JITCompiler {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		string::init(global);
		uri::init(global);
		JITCompiler {
			context: Context::new(),
			global: global,
			scopes: vec!(Scope {
				this: global,
				vars: global
			})
		}
	}
	#[inline(always)]
	fn set_global(&mut self, name:String, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	#[inline(always)]
	fn get_global(&self, name:String) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self, this:Value) -> Scope {
		let scope = Scope {
			this: this,
			vars: ValueData::new_obj(None)
		};
		self.scopes.push(scope);
		scope
	}
	#[inline(always)]
	fn destroy_scope(&mut self) -> Scope {
		self.scopes.pop().unwrap()
	}
	fn compile(&self, expr: &Expr) -> Box<Function> {
		self.with_builder(|| {
			let valuedata_t = Types::get_int();
			let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
			let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
			let default_sig_t = Type::create_signature(CDECL, &*value_t, &[]);
			fn compile_value(func:&Function, expr: &Expr) -> Box<jit::Value> {
				let valuedata_t = Types::get_int();
				let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
				let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
				match expr.def {
					ConstExpr(CNull) => {
						fn create_null_value() -> Value {
							Gc::new(VNull)
						}
						let create_null_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_null_value", create_null_value, &*create_null_sig, &[])
					},
					ConstExpr(CUndefined) => {
						fn create_undef_value() -> Value {
							Gc::new(VUndefined)
						}
						let create_undef_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_undef_value", create_undef_value, &*create_undef_sig, &[])
					},
					ConstExpr(CBool(v)) => {
						let create_bool_value = to_value::<bool>;
						let val = func.constant_int32_as_type(v as i32, &*Types::get_bool());
						let create_bool_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_bool()]);
						func.insn_call_native1("create_bool_value", create_bool_value, &*create_bool_sig, &[&*val])
					},
					ConstExpr(CNum(n)) => {
						let create_number_value = to_value::<f64>;
						let val = func.constant_float64(n);
						let create_number_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_float64()]);
						func.insn_call_native1("create_number_value", create_number_value, &* create_number_sig, &[&*val])
					},
					ReturnExpr(None) => {
						fn create_undef_value() -> Value {
							Gc::new(VUndefined)
						}
						func.insn_default_return();
						let create_undef_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_undef_value", create_undef_value, &*create_undef_sig, &[])
					},
					ReturnExpr(Some(ref ret)) => {
						let i_ret = compile_value(func, *ret);
						func.insn_return(i_ret);
						i_ret
					},
					UnaryOpExpr(op, ref a) => {
						let i_a = compile_value(func, *a);
						let unop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t]);
						let (name, op_func) = match op {
							UnaryPlus => return i_a,
							UnaryMinus => {
								fn neg_value(a:Value) -> Value {
									Gc::new(-a.borrow())
								}
								("neg", neg_value)
							}
							UnaryNot => {
								fn not_value(a: Value) -> Value {
									Gc::new(!a.borrow())
								}
								("not", not_value)
							},
							_ => fail!("Unimplemented {}", op)
						};
						func.insn_call_native1(name, op_func, unop_sig, &[&*i_a])
					},
					BinOpExpr(op, ref a, ref b) => {
						let i_a = compile_value(func, *a);
						let i_b = compile_value(func, *b);
						let binop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t, &*value_t]);

						let (name, op_func) = match op {
							BinNum(OpAdd) => {
								fn add_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() + *b.borrow())
								}
								("add", add_values)
							},
							BinNum(OpSub) => {
								fn sub_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() - *b.borrow())
								}
								("sub", sub_values)
							},
							BinNum(OpMul) => {
								fn mul_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() * *b.borrow())
								}
								("mul", mul_values)
							},
							BinNum(OpDiv) => {
								fn div_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() / *b.borrow())
								}
								("div", div_values)
							},
							BinNum(OpMod) => {
								fn mod_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() % *b.borrow())
								}
								("mod", mod_values)
							},
							BinBit(BitAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() & *b.borrow())
								}
								("and", and_values)
							},
							BinBit(BitOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() | *b.borrow())
								}
								("or", or_values)
							},
							BinBit(BitXor) => {
								fn xor_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() ^ *b.borrow())
								}
								("xor", xor_values)
							},
							BinBit(BitShl) => {
								fn shl_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() << *b.borrow())
								}
								("shl", shl_values)
							},
							BinBit(BitShr) => {
								fn shr_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() >> *b.borrow())
								}
								("shr", shr_values)
							},
							BinLog(LogOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() || b.borrow().is_true())
								}
								("or", or_values)
							},
							BinLog(LogAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() && b.borrow().is_true())
								}
								("and", and_values)
							},
							BinComp(CompEqual) | BinComp(CompStrictEqual) => {
								fn eq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() == b.borrow())
								}
								("eq", eq_values)
							},
							BinComp(CompNotEqual) | BinComp(CompStrictNotEqual) => {
								fn neq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() != b.borrow())
								}
								("neq", neq_values)
							},
							BinComp(CompLessThan) => {
								fn lt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() < b.borrow())
								}
								("lt", lt_values)
							},
							BinComp(CompLessThanOrEqual) => {
								fn lte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() <= b.borrow())
								}
								("lte", lte_values)
							},
							BinComp(CompGreaterThan) => {
								fn gt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() > b.borrow())
								}
								("gt", gt_values)
							},
							BinComp(CompGreaterThanOrEqual) => {
								fn gte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() >= b.borrow())
								}
								("gte", gte_values)
							}
						};
						func.insn_call_native2(name, op_func, binop_sig, &[&*i_a, &*i_b])
					},
					_ => fail!("Unimplemented {}", expr)
				}
			}
			let func = self.context.create_function(&*default_sig_t);
			let value = compile_value(func, expr);
			func.insn_return(&*value);
			func.compile();
			func
		})
	}
	fn run(&mut self, comp:Box<Function>) -> ResultValue {
		let func: fn() -> Value = comp.closure();
		return Ok(func());
	}
}<|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, 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;
// use std::libc::types::os::arch::posix88::{pid_t};
// use std::libc::{c_int};

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) {
        // I thought this would declare a new process group that I can use
        // to put background processes in later so they didn't get sent the
        // SIGINT signal. Needless to say it doesn't work...
        // #[nolink]
        // extern { fn setpgid(pid : pid_t, pgid: pid_t) -> c_int; }
        // unsafe { println!("setting pgid... {:d}", setpgid(0, 0)); }

        let mut stdin = BufferedReader::new(stdin());

        let (port_PID, chan_PID) : (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);

            let mut last_pid : i32 = 1;
            loop {
                match port_PID.try_recv() {
                    Some(status) if status == exit_code => {
                        listener.unregister(Interrupt);
                        // println!("Matched exit code");
                        return;
                    }
                    // These two would work if the SIGINT signal didn't get sent
                    // to everything... They're not necessary right now but I don't
                    // want to take them out in case we end up using the PID later.
                    Some(pid) if pid == nop_code => {
                        // println!("Matched nop code");
                        chan_status.send(nop_code);    // To complete process
                    }
                    Some(pid) => {
                        // println!("Matched PID")
                        let mut my_pid : i32;
                        unsafe { my_pid = std::libc::getpid(); }
                        if (pid != my_pid) {
                            last_pid = pid;
                            // println!("Changed PID to {:d}", last_pid);
                        }
                            chan_status.send(nop_code);   // To complete process
                    }
                    None    => {}
                }

                // This MIGHT kill the process if it wasn't already killed along with everything else...
                // match listener.port.try_recv() {
                //     Some(Interrupt) => {
                //         println!("Killing {:d}", last_pid);
                //         unsafe {
                //             let result : i32 = std::libc::funcs::posix88::signal::kill(last_pid, 0);
                //         }
                //     }
                //     _ => {}
                // }
            }
        });

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

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

            // This block handles if there are no spaces around pipe.
            let lineClone = line.clone();
            for character in lineClone.chars() {
                if character == '<' {
                    line = line.slice(0, i).to_owned() + " < " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                } else if character == '>' {
                    line = line.slice(0, i).to_owned() + " > " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                } else if character == '|' {
                    line = line.slice(0, i).to_owned() + " | " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                }
                i = i + 1;
            }

            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_PID.send(nop_code);
                                  continue; }
                "exit"      =>  { chan_PID.send(exit_code);
                                  return; }  // Exit
                "cd"        =>  { Shell::run_check_mode(background, Shell::run_cd(params), &chan_PID); }
                "history"   =>  { Shell::run_check_mode(background, Shell::run_history(params, self.cmd_history), &chan_PID); }
                _           =>  { Shell::run_check_mode(background, Shell::run_cmdline(params), &chan_PID); }
            }

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

    fn run_check_mode(background: bool, f: proc(), chan_PID : &Chan<i32>) {
        let (port_temp, chan_temp) : (Port<i32>, Chan<i32>) = Chan::new();
        if background {
            spawn(proc() {
                // This should have moved the process into a new process group.
                // It didn't work though. Still got SIGINT from the interrupt.
                // #[nolink]
                // extern { fn setpgid(pid : pid_t, pgid: pid_t) -> c_int; }
                // unsafe { println!("Setting pgid... {:d}", setpgid(std::libc::getpid(), 0)); }

                f();
            });
            chan_PID.send(nop_code);  // Don't send PID.
        } else {
            spawn(proc() {
                f();
                unsafe {
                    // println!("Sending new PID of {:d}", std::libc::getpid());
                    chan_temp.send(std::libc::getpid());
                }
            });
            chan_PID.send(port_temp.recv());
        }
    }

    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 => { /*Do nothing*/ }
                }
            }
        };
    }

    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) }
            };

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

            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 = false;
        return cmd_line.split(' ').filter_map(|x| if x == ">" || x == "<" {
            activeRedirect = true;
            None
        } else if x == "" {
            None
        } else if activeRedirect {
            activeRedirect = false;
            None
        } else {
            Some(x.to_owned())
        }).to_owned_vec();
    }

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

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 all PID tracking and handling.<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;
                    }
                    Some(response) => { chan_status.send(response); }   // To complete process
                    None    => {}
                }
            }
        });

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

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

            // This block handles if there are no spaces around pipe.
            let lineClone = line.clone();
            for character in lineClone.chars() {
                if character == '<' {
                    line = line.slice(0, i).to_owned() + " < " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                } else if character == '>' {
                    line = line.slice(0, i).to_owned() + " > " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                } else if character == '|' {
                    line = line.slice(0, i).to_owned() + " | " + line.slice(i+1, line.char_len()).to_owned();
                    i = i + 2;
                }
                i = i + 1;
            }

            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);
                                  return; }  // 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);  // Don't wait til f returns.
        } else {
            spawn(proc() {
                f();
                chan_temp.send(nop_code); // Wait til f returns.
            });
            chan_func.send(port_temp.recv());
        }
    }

    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 => { /*Do nothing*/ }
                }
            }
        };
    }

    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) }
            };

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

            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 = false;
        return cmd_line.split(' ').filter_map(|x| if x == ">" || x == "<" {
            activeRedirect = true;
            None
        } else if x == "" {
            None
        } else if activeRedirect {
            activeRedirect = false;
            None
        } else {
            Some(x.to_owned())
        }).to_owned_vec();
    }

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

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>//
// 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, File, Truncate, Write};
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       : Option<i32>,
    stdout      : Option<i32>,
    exit_status : Option<process::ProcessExit>,
}
impl CmdProcess {
    fn new(command: &str,
           args: ~[~str],
           stdin : Option<i32>,
           stdout : Option<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  : self.stdin,
            out_fd : 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] = self.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('>') {
            self.parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            self.parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            let argv: ~[~str] = self.split_words(cmd_line);
            if argv.len() > 0 {
                self.parse_process(argv);
            }
        }
    }

    fn parse_process(&mut self, mut argv : ~[~str]) -> Option<Process>{
        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);
                None
            }
            else {
                self.make_process(program, argv, Some(0), Some(1))
            }
        }
        else {
            self.make_process(program, argv, Some(0), Some(1))
        }
    }

    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        let one = pair[0].trim();
        if pair.len() > 1 {
            let mut argv: ~[~str] = self.split_words(one);
            if argv.len() > 0 {
                let two = self.parse_pipeline(pair[1].trim());
                let program: ~str = argv.remove(0);
                if cmd_line.contains_char('|') {
                    self.pipe_input(program, argv, two)
                }
                else {
                    self.pipe_input(program, argv, two)
                }
            }
            else {
                None
            }
        }
        else {
            self.make_first_process(one)
        }
    }

    fn pipe_input(&mut self, program : &str, argv : ~[~str], two : Option<~run::Process>)
            -> Option<~run::Process> {
        match CmdProcess::new(program, argv, None, Some(1)) {
            Some(mut cmdprocess) => {
                match cmdprocess.run() {
                    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 => {
                                Some(~process)
                            }
                        }
                    }
                    None => {
                        println!("Failed spawning a process for {:s}.", program) ;
                        None
                    }
                }
            }
            None => {
                println!("{:s} is not a command.", program);
                None
            }
        }
    }

    // For pipeline - first item. Returns Maybe Process.
    fn make_first_process(&mut self, cmd_line : &str) -> Option<~run::Process>{
        let mut argv: ~[~str] = self.split_words(cmd_line);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match CmdProcess::new(program, argv, None, None) {
                Some(mut cmdprocess) => {
                    match cmdprocess.run() {
                        Some(process) => {
                            Some(~process)
                        }
                        None => {
                            println!("Failed spawning a process for {:s}.", program);
                            None
                        }
                    }
                }
                None => {
                    println!("{:s} is not a command.", program) ;
                    None
                }
            }
        }
        else {None}
    }

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

    fn parse_r_redirect(&mut self, cmd_line : &str) {
        let pair : ~[&str] = cmd_line.rsplit('>').collect();
        let file = pair[0].trim();
        let command = pair[1].trim();
        let mut argv: ~[~str] = self.split_words(command);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match self.make_process(program, argv, None, None) {
                Some(mut process) => {
                    self.write_output_to_file(
                        process.finish_with_output(),
                        file);
                }
                None => {
                    println!("Failed spawning a process for {:s}.", command)
                }
            }
        }
    }

    fn write_output_to_file(&mut self,
                            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_l_redirect(&mut self, cmd_line : &str) {
        let pair : ~[&str] = cmd_line.rsplit('<').collect();
        let filename = pair[0].trim();
        let command = pair[1].trim();
        let mut argv: ~[~str] = self.split_words(command);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match self.make_process(program, argv, 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 => {
                    println!("Failed spawning a process for {:s}.", command)
                }
            }
        }
    }

    fn make_process(&mut self,
                    program : ~str,
                    argv : ~[~str],
                    stdin: Option<i32>,
                    stdout: Option<i32>) -> Option<run::Process> {
        match CmdProcess::new(program, argv, stdin, stdout) {
            Some(mut cmdprocess) => {
                cmdprocess.run()
            }
            None => { None }
        }
    }

    fn make_bg_process(&mut self, program : ~str, argv : ~[~str]) {
        match BackgroundProcess::new(program, argv) {
            Some(mut process) => {
                process.run();
                self.processes.push(~process);
            }
            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 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>more cleanup<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, File, Truncate, Write};
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       : Option<i32>,
    stdout      : Option<i32>,
    exit_status : Option<process::ProcessExit>,
}
impl CmdProcess {
    fn new(command: &str,
           args: ~[~str],
           stdin : Option<i32>,
           stdout : Option<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  : self.stdin,
            out_fd : 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] = self.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('>') {
            self.parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            self.parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            let argv: ~[~str] = self.split_words(cmd_line);
            if argv.len() > 0 {
                self.parse_process(argv);
            }
        }
    }

    fn parse_process(&mut self, mut argv : ~[~str]) -> Option<Process>{
        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);
                None
            }
            else {
                self.make_process(program, argv, Some(0), Some(1))
            }
        }
        else {
            self.make_process(program, argv, Some(0), Some(1))
        }
    }

    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        let one = pair[0].trim();
        if pair.len() > 1 {
            let mut argv: ~[~str] = self.split_words(one);
            if argv.len() > 0 {
                let two = self.parse_pipeline(pair[1].trim());
                let program: ~str = argv.remove(0);
                if cmd_line.contains_char('|') {
                    self.pipe_input(program, argv, two)
                }
                else {
                    self.pipe_input(program, argv, two)
                }
            }
            else {
                None
            }
        }
        else {
            self.make_first_process(one)
        }
    }

    fn pipe_input(&mut self, program : &str, argv : ~[~str], two : Option<~run::Process>)
            -> Option<~run::Process> {
        match CmdProcess::new(program, argv, None, Some(1)) {
            Some(mut cmdprocess) => {
                match cmdprocess.run() {
                    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 => {
                                Some(~process)
                            }
                        }
                    }
                    None => {
                        println!("Failed spawning a process for {:s}.", program) ;
                        None
                    }
                }
            }
            None => {
                println!("{:s} is not a command.", program);
                None
            }
        }
    }

    // For pipeline - first item. Returns Maybe Process.
    fn make_first_process(&mut self, cmd_line : &str) -> Option<~run::Process>{
        let mut argv: ~[~str] = self.split_words(cmd_line);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match self.make_process(program, argv, None, None) {
                Some(process) => {
                    Some(~process)
                }
                None => {
                    println!("Failed spawning a process for {:s}.", program);
                    None
                }
            }
        }
        else {None}
    }

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

    fn parse_r_redirect(&mut self, cmd_line : &str) {
        let pair : ~[&str] = cmd_line.rsplit('>').collect();
        let file = pair[0].trim();
        let command = pair[1].trim();
        let mut argv: ~[~str] = self.split_words(command);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match self.make_process(program, argv, None, None) {
                Some(mut process) => {
                    self.write_output_to_file(
                        process.finish_with_output(),
                        file);
                }
                None => {
                    println!("Failed spawning a process for {:s}.", command)
                }
            }
        }
    }

    fn write_output_to_file(&mut self,
                            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_l_redirect(&mut self, cmd_line : &str) {
        let pair : ~[&str] = cmd_line.rsplit('<').collect();
        let filename = pair[0].trim();
        let command = pair[1].trim();
        let mut argv: ~[~str] = self.split_words(command);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            match self.make_process(program, argv, 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 => {
                    println!("Failed spawning a process for {:s}.", command)
                }
            }
        }
    }

    fn make_process(&mut self,
                    program : &str,
                    argv : ~[~str],
                    stdin: Option<i32>,
                    stdout: Option<i32>) -> Option<run::Process> {
        match CmdProcess::new(program, argv, stdin, stdout) {
            Some(mut cmdprocess) => {
                cmdprocess.run()
            }
            None => { None }
        }
    }

    fn make_bg_process(&mut self, program : &str, argv : ~[~str]) {
        match BackgroundProcess::new(program, argv) {
            Some(mut process) => {
                process.run();
                self.processes.push(~process);
            }
            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 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>//! Shared mathematical utility functions.

use std::cmp::max;

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained with in the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(
    width: u32,
    height: u32,
    nwidth: u32,
    nheight: u32,
    fill: bool,
) -> (u32, u32) {
    let wratio = nwidth as f64 / width as f64;
    let hratio = nheight as f64 / height as f64;

    let ratio = if fill {
        f64::max(wratio, hratio)
    } else {
        f64::min(wratio, hratio)
    };

    let nw = max((width as f64 * ratio).round() as u64, 1);
    let nh = max((height as f64 * ratio).round() as u64, 1);

    if nw > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / width as f64;
        (u32::MAX, max((height as f64 * ratio).round() as u32, 1))
    } else if nh > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / height as f64;
        (max((width as f64 * ratio).round() as u32, 1), u32::MAX)
    } else {
        (nw as u32, nh as u32)
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64).round() as u32
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64).round() as u32
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_never_rounds_to_zero() {
        let result = super::resize_dimensions(1, 150, 128, 128, false);
        assert!(result.0 > 0);
        assert!(result.1 > 0);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }

    #[test]
    fn resize_rounds() {
        // Only truncation will result in (3840, 2229) and (2160, 3719)
        let result = super::resize_dimensions(4264, 2476, 3840, 2160, true);
        assert_eq!(result, (3840, 2230));

        let result = super::resize_dimensions(2476, 4264, 2160, 3840, false);
        assert_eq!(result, (2160, 3720));
    }

    #[test]
    fn resize_handles_zero() {
        let result = super::resize_dimensions(0, 100, 100, 100, false);
        assert_eq!(result, (1, 100));

        let result = super::resize_dimensions(100, 0, 100, 100, false);
        assert_eq!(result, (100, 1));

        let result = super::resize_dimensions(100, 100, 0, 100, false);
        assert_eq!(result, (1, 1));

        let result = super::resize_dimensions(100, 100, 100, 0, false);
        assert_eq!(result, (1, 1));
    }
}
<commit_msg>Succeed early on unusual resize quickcheck case<commit_after>//! Shared mathematical utility functions.

use std::cmp::max;

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained within the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(
    width: u32,
    height: u32,
    nwidth: u32,
    nheight: u32,
    fill: bool,
) -> (u32, u32) {
    let wratio = nwidth as f64 / width as f64;
    let hratio = nheight as f64 / height as f64;

    let ratio = if fill {
        f64::max(wratio, hratio)
    } else {
        f64::min(wratio, hratio)
    };

    let nw = max((width as f64 * ratio).round() as u64, 1);
    let nh = max((height as f64 * ratio).round() as u64, 1);

    if nw > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / width as f64;
        (u32::MAX, max((height as f64 * ratio).round() as u32, 1))
    } else if nh > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / height as f64;
        (max((width as f64 * ratio).round() as u32, 1), u32::MAX)
    } else {
        (nw as u32, nh as u32)
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            if new_w as u64 * 400u64 >= old_w as u64 * u64::from(u32::MAX) { return true; }

            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            let exact = (400 as f64 * new_w as f64 / old_w as f64).round() as u32;
            result.0 == new_w && result.1 == exact.max(1)
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            if 400u64 * new_h as u64 >= old_h as u64 * u64::from(u32::MAX) { return true; }

            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            let exact = (400 as f64 * new_h as f64 / old_h as f64).round() as u32;
            result.1 == new_h && result.0 == exact.max(1)
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_never_rounds_to_zero() {
        let result = super::resize_dimensions(1, 150, 128, 128, false);
        assert!(result.0 > 0);
        assert!(result.1 > 0);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }

    #[test]
    fn resize_rounds() {
        // Only truncation will result in (3840, 2229) and (2160, 3719)
        let result = super::resize_dimensions(4264, 2476, 3840, 2160, true);
        assert_eq!(result, (3840, 2230));

        let result = super::resize_dimensions(2476, 4264, 2160, 3840, false);
        assert_eq!(result, (2160, 3720));
    }

    #[test]
    fn resize_handles_zero() {
        let result = super::resize_dimensions(0, 100, 100, 100, false);
        assert_eq!(result, (1, 100));

        let result = super::resize_dimensions(100, 0, 100, 100, false);
        assert_eq!(result, (100, 1));

        let result = super::resize_dimensions(100, 100, 0, 100, false);
        assert_eq!(result, (1, 1));

        let result = super::resize_dimensions(100, 100, 100, 0, false);
        assert_eq!(result, (1, 1));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#[macro_use]
extern crate gj;
extern crate gjio;
use gj::{EventLoop, Promise};
use gjio::{AsyncRead, AsyncWrite};

#[test]
fn hello() {
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10000").unwrap();
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let _write_promise = listener.accept().then(move |mut stream| {
            stream.write(vec![0,1,2,3,4,5])
        });

        let read_promise = address.connect().then(move |mut stream| {
            stream.read(vec![0u8; 6], 6)
        });

        let (buf, _) = try!(read_promise.wait(wait_scope, &mut event_port));

        assert_eq!(&buf[..], [0,1,2,3,4,5]);
        Ok(())
    }).expect("top level");
}

#[test]
fn echo() {
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10001").unwrap();
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let _server_promise = listener.accept().then(move |mut stream| {
            stream.read(vec![0u8; 6], 6).lift().then(move |( mut v, _)| {
                assert_eq!(&v[..], [7,6,5,4,3,2]);
                for x in &mut v {
                    *x += 1;
                }
                stream.write(v).lift()
            })
        });

        let client_promise = address.connect().then(move |mut stream| {
            stream.write(vec![7,6,5,4,3,2]).then(move |v| {
                stream.read(v, 6)
            })
        });

        let (buf, _) = try!(client_promise.wait(wait_scope, &mut event_port));
        assert_eq!(&buf[..], [8,7,6,5,4,3]);
        Ok(())
    }).expect("top level");
}

#[test]
fn timers() {
    use std::rc::Rc;
    use std::cell::Cell;

    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let timer = event_port.get_timer();

        let counter = Rc::new(Cell::new(0u32));
        let counter1 = counter.clone();
        let counter2 = counter.clone();
        let counter3 = counter.clone();

        let p2 = timer.after_delay(::std::time::Duration::from_millis(100)).map(move |()| {
            assert_eq!(counter2.get(), 1);
            counter2.set(2);
            Ok(())
        });

        let p3 = timer.after_delay(::std::time::Duration::from_millis(500)).map(move |()| {
            assert_eq!(counter3.get(), 2);
            counter3.set(3);
            Ok(())
        });

        let p1 = timer.after_delay(::std::time::Duration::from_millis(10)).map(move |()| {
            assert_eq!(counter1.get(), 0);
            counter1.set(1);
            Ok(())
        });

        try!(Promise::all(vec![p1,p2,p3].into_iter()).wait(wait_scope, &mut event_port));

        assert_eq!(counter.get(), 3);
        Ok(())
    }).expect("top_level");
}

/*
#[cfg(unix)]
#[test]
fn deregister_dupped_unix() {
    use gjmio::unix;
    // At one point, this panicked on Linux with "invalid handle idx".
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjmio::EventPort::new());
        let (stream1, stream2) = try!(unix::Stream::new_pair());
        let stream1_dupped = try!(stream1.try_clone());
        drop(stream1);

        let promise1 = stream1_dupped.read(vec![0u8; 6], 6);
        let _promise2 = stream2.write(vec![1,2,3,4,5,6]);

        let _ = promise1.lift::<::std::io::Error>().wait(wait_scope, &mut event_port);
        Ok(())
    }).unwrap();
}

#[test]
fn deregister_dupped_tcp() {
    // At one point, this panicked on Linux with "invalid handle idx".
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjmio::EventPort::new());
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10002").unwrap();
        let listener = tcp::Listener::bind(addr).unwrap();

        let server_promise = listener.accept().lift::<::std::io::Error>().map(move |(_, stream1)| {
            Ok(Some(stream1))
        });
        let client_promise = tcp::Stream::connect(addr).lift::<::std::io::Error>().map(|stream2| Ok(Some(stream2)));
        let promise = Promise::all(vec![server_promise, client_promise].into_iter()).then(|mut streams| {
            let stream0 = streams[0].take().unwrap();
            let stream1 = streams[1].take().unwrap();
            let stream0_dupped = pry!(stream0.try_clone());
            drop(stream0);

            let promise1 = stream0_dupped.read(vec![0u8; 6], 6).lift();
            let promise2 = stream1.write(vec![1,2,3,4,5,6]).lift();

            promise2.then(|_| promise1)
        });

        let _ = promise.wait(wait_scope, &mut event_port);
        Ok(())
    }).unwrap();
}
*/
<commit_msg>timer cancellation test<commit_after>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#[macro_use]
extern crate gj;
extern crate gjio;
use gj::{EventLoop, Promise};
use gjio::{AsyncRead, AsyncWrite};

#[test]
fn hello() {
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10000").unwrap();
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let _write_promise = listener.accept().then(move |mut stream| {
            stream.write(vec![0,1,2,3,4,5])
        });

        let read_promise = address.connect().then(move |mut stream| {
            stream.read(vec![0u8; 6], 6)
        });

        let (buf, _) = try!(read_promise.wait(wait_scope, &mut event_port));

        assert_eq!(&buf[..], [0,1,2,3,4,5]);
        Ok(())
    }).expect("top level");
}

#[test]
fn echo() {
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10001").unwrap();
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let _server_promise = listener.accept().then(move |mut stream| {
            stream.read(vec![0u8; 6], 6).lift().then(move |( mut v, _)| {
                assert_eq!(&v[..], [7,6,5,4,3,2]);
                for x in &mut v {
                    *x += 1;
                }
                stream.write(v).lift()
            })
        });

        let client_promise = address.connect().then(move |mut stream| {
            stream.write(vec![7,6,5,4,3,2]).then(move |v| {
                stream.read(v, 6)
            })
        });

        let (buf, _) = try!(client_promise.wait(wait_scope, &mut event_port));
        assert_eq!(&buf[..], [8,7,6,5,4,3]);
        Ok(())
    }).expect("top level");
}

#[test]
fn timers_ordering() {
    use std::rc::Rc;
    use std::cell::Cell;

    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let timer = event_port.get_timer();

        let counter = Rc::new(Cell::new(0u32));
        let counter1 = counter.clone();
        let counter2 = counter.clone();
        let counter3 = counter.clone();

        let p2 = timer.after_delay(::std::time::Duration::from_millis(100)).map(move |()| {
            assert_eq!(counter2.get(), 1);
            counter2.set(2);
            Ok(())
        });

        let p3 = timer.after_delay(::std::time::Duration::from_millis(500)).map(move |()| {
            assert_eq!(counter3.get(), 2);
            counter3.set(3);
            Ok(())
        });

        let p1 = timer.after_delay(::std::time::Duration::from_millis(10)).map(move |()| {
            assert_eq!(counter1.get(), 0);
            counter1.set(1);
            Ok(())
        });

        try!(Promise::all(vec![p1,p2,p3].into_iter()).wait(wait_scope, &mut event_port));

        assert_eq!(counter.get(), 3);
        Ok(())
    }).expect("top_level");
}

#[test]
fn timer_cancelation() {
    use std::rc::Rc;
    use std::cell::Cell;

    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjio::EventPort::new());
        let timer = event_port.get_timer();

        let executed = Rc::new(Cell::new(false));
        let executed1 = executed.clone();

        let p1 = timer.after_delay(::std::time::Duration::from_millis(100)).map(move |()| {
            executed1.set(true);
            Ok(())
        });

        let p2 = timer.after_delay(::std::time::Duration::from_millis(1));
        let p3 = timer.after_delay(::std::time::Duration::from_millis(200));

        assert_eq!(executed.get(), false);
        try!(p2.wait(wait_scope, &mut event_port));
        assert_eq!(executed.get(), false);
        drop(p1);
        try!(p3.wait(wait_scope, &mut event_port));
        assert_eq!(executed.get(), false);

        Ok(())
    }).expect("top_level");
}

/*
#[cfg(unix)]
#[test]
fn deregister_dupped_unix() {
    use gjmio::unix;
    // At one point, this panicked on Linux with "invalid handle idx".
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjmio::EventPort::new());
        let (stream1, stream2) = try!(unix::Stream::new_pair());
        let stream1_dupped = try!(stream1.try_clone());
        drop(stream1);

        let promise1 = stream1_dupped.read(vec![0u8; 6], 6);
        let _promise2 = stream2.write(vec![1,2,3,4,5,6]);

        let _ = promise1.lift::<::std::io::Error>().wait(wait_scope, &mut event_port);
        Ok(())
    }).unwrap();
}

#[test]
fn deregister_dupped_tcp() {
    // At one point, this panicked on Linux with "invalid handle idx".
    EventLoop::top_level(|wait_scope| -> Result<(), ::std::io::Error> {
        let mut event_port = try!(gjmio::EventPort::new());
        let addr = ::std::str::FromStr::from_str("127.0.0.1:10002").unwrap();
        let listener = tcp::Listener::bind(addr).unwrap();

        let server_promise = listener.accept().lift::<::std::io::Error>().map(move |(_, stream1)| {
            Ok(Some(stream1))
        });
        let client_promise = tcp::Stream::connect(addr).lift::<::std::io::Error>().map(|stream2| Ok(Some(stream2)));
        let promise = Promise::all(vec![server_promise, client_promise].into_iter()).then(|mut streams| {
            let stream0 = streams[0].take().unwrap();
            let stream1 = streams[1].take().unwrap();
            let stream0_dupped = pry!(stream0.try_clone());
            drop(stream0);

            let promise1 = stream0_dupped.read(vec![0u8; 6], 6).lift();
            let promise2 = stream1.write(vec![1,2,3,4,5,6]).lift();

            promise2.then(|_| promise1)
        });

        let _ = promise.wait(wait_scope, &mut event_port);
        Ok(())
    }).unwrap();
}
*/
<|endoftext|>