<commit_before>use std::kinds::marker::NoCopy;
use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	nocopy: NoCopy,
}

pub struct Method {
	nocopy: NoCopy,
}

pub struct Class {
	nocopy: NoCopy,
}

pub struct Object {
	nocopy: NoCopy,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> *const Class;
	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
	pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn arguments(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}

	pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
		method_exchangeImplementations(self, other);
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_null() {
			None
		} else {
			Some(unsafe { &*cls })
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		let method = unsafe {
			class_getInstanceMethod(self, sel)
		};
		if method.is_null() {
			None
		} else {
			Some(unsafe { &*method })
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name)
		});
		if ivar.is_null() {
			None
		} else {
			Some(unsafe { &*ivar })
		}
	}

	pub fn instance_variables(&self) -> Vec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			let vec = buf_as_slice(ivars as *const _, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

pub trait Message { }

impl Message for Object { }

impl Message for Class { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}
<commit_msg>Implemented Eq for Class.<commit_after>use std::kinds::marker::NoCopy;
use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	nocopy: NoCopy,
}

pub struct Method {
	nocopy: NoCopy,
}

pub struct Class {
	nocopy: NoCopy,
}

pub struct Object {
	nocopy: NoCopy,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> *const Class;
	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
	pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn arguments(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}

	pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
		method_exchangeImplementations(self, other);
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_null() {
			None
		} else {
			Some(unsafe { &*cls })
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		let method = unsafe {
			class_getInstanceMethod(self, sel)
		};
		if method.is_null() {
			None
		} else {
			Some(unsafe { &*method })
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name)
		});
		if ivar.is_null() {
			None
		} else {
			Some(unsafe { &*ivar })
		}
	}

	pub fn instance_variables(&self) -> Vec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			let vec = buf_as_slice(ivars as *const _, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

impl PartialEq for Class {
	fn eq(&self, other: &Class) -> bool {
		self as *const Class == other as *const Class
	}
}

impl Eq for Class { }

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

pub trait Message { }

impl Message for Object { }

impl Message for Class { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}
<|endoftext|>


<commit_before>#![crate_name = "uu_chgrp"]

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown};
pub use uucore::entries;
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::io::prelude::*;
use std::io::Result as IOResult;
use std::io::Error as IOError;

use std::fs;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::path::Path;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &'static str = "chgrp [OPTION]... GROUP FILE...\n or :  chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "Change the group of each FILE to GROUP.";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    if matches.free.len() < 1 {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let dest_gid: gid_t;
    let mut files;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = meta.gid();
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
        files = matches.free;
    } else {
        match entries::grp2gid(&matches.free[0]) {
            Ok(g) => {
                dest_gid = g;
            }
            _ => {
                show_info!("invalid group: {}", matches.free[0].as_str());
                return 1;
            }
        }
        files = matches.free;
        files.remove(0);
    }

    let executor = Chgrper {
        bit_flag: bit_flag,
        dest_gid: dest_gid,
        verbosity: verbosity,
        recursive: recursive,
        dereference: derefer != 0,
        preserve_root: preserve_root,
        files: files,
    };
    executor.exec()
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

struct Chgrper {
    dest_gid: gid_t,
    bit_flag: u8,
    verbosity: Verbosity,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chgrper {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chgrp<P: AsRef<Path>>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            } else {
                lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(IOError::last_os_error())
        }
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = self.wrap_chgrp(path, &meta, follow_arg);

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            ret = self.wrap_chgrp(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chgrp<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_gid = self.dest_gid;
        let path = path.as_ref();
        if let Err(e) = self.chgrp(path, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing group of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!("failed to change group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!("changed group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!("group of {} retained as {}",
                         path.display(),
                         entries::gid2grp(dest_gid).unwrap());
            }
        }
        ret
    }
}
<commit_msg>chgrp: detect bind mounted root<commit_after>#![crate_name = "uu_chgrp"]

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown};
pub use uucore::entries;
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::io::prelude::*;
use std::io::Result as IOResult;
use std::io::Error as IOError;

use std::fs;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::path::Path;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &'static str = "chgrp [OPTION]... GROUP FILE...\n or :  chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "Change the group of each FILE to GROUP.";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    if matches.free.len() < 1 {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let dest_gid: gid_t;
    let mut files;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = meta.gid();
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
        files = matches.free;
    } else {
        match entries::grp2gid(&matches.free[0]) {
            Ok(g) => {
                dest_gid = g;
            }
            _ => {
                show_info!("invalid group: {}", matches.free[0].as_str());
                return 1;
            }
        }
        files = matches.free;
        files.remove(0);
    }

    let executor = Chgrper {
        bit_flag: bit_flag,
        dest_gid: dest_gid,
        verbosity: verbosity,
        recursive: recursive,
        dereference: derefer != 0,
        preserve_root: preserve_root,
        files: files,
    };
    executor.exec()
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

struct Chgrper {
    dest_gid: gid_t,
    bit_flag: u8,
    verbosity: Verbosity,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chgrper {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chgrp<P: AsRef<Path>>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            } else {
                lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(IOError::last_os_error())
        }
    }

    #[cfg(windows)]
    fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
        false
    }

    #[cfg(unix)]
    fn is_bind_root<P: AsRef<Path>>(&self, path: P) -> bool {
        use std::os::unix::fs::MetadataExt;

        // FIXME: remove unwrap here
        let given = std::fs::metadata(path).unwrap();
        let root = std::fs::metadata("/").unwrap();
        given.dev() == root.dev() && given.ino() == root.ino()
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() || self.is_bind_root(p) {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = self.wrap_chgrp(path, &meta, follow_arg);

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            ret = self.wrap_chgrp(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chgrp<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_gid = self.dest_gid;
        let path = path.as_ref();
        if let Err(e) = self.chgrp(path, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing group of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!("failed to change group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!("changed group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!("group of {} retained as {}",
                         path.display(),
                         entries::gid2grp(dest_gid).unwrap());
            }
        }
        ret
    }
}
<|endoftext|>


<commit_before>extern crate regex;

use colored::*;
use nom::IResult;
use std::process::exit;
use std::path::PathBuf;
use regex::Regex;
use error::check_regex;

pub fn get_excludes(cli_excludes: Option<&str>) -> Regex {
    //lazy_static! { // TODO fix this with an enum?
    let regex_git: Regex =
        Regex::new(r".*?\.(stats|conf|h|cache.*|dat|pc|info)$")
        .unwrap();
    //}
    match cli_excludes {
        Some(x) => { x.to_owned().push_str(r"|\.git"); check_regex(x) }
        _ => regex_git,

    }
}

pub fn get_depth(depth_from_cli: Option<&str>) -> u8 {
    if let Some(n) = depth_from_cli {
        n.parse::<u8>().expect("Please enter a positive whole number")
    }
    else {
        2
    }
}

pub fn get_num(num_from_cli: Option<&str>) -> usize {
    if let Some(num) = num_from_cli {
        num.parse::<usize>().expect("Please enter a positive whole number")
    }
    else {
        20
    }
}

pub fn get_dir(path_from_cli: Option<&str>) -> PathBuf {
    if let Some(read) = path_from_cli {
        PathBuf::from(read)
    }
    else {
        PathBuf::from(".")
    }
}

pub fn threshhold(s: Option<&str>) -> Option<u64> {
    s.map(pre_threshhold)
}

fn pre_threshhold(t_from_cli: &str) -> u64 {
    match get_threshhold(t_from_cli.as_bytes()) {
            IResult::Done(_,n) => n,
            _ => { eprintln!("{}: failed to parse threshhold. defaulting to 1M", "Warning".yellow()) ; 1048576 },
            }
}

fn to_u64(nums: Vec<char>, size_tag: &[u8]) -> u64 {
    let pre: String = nums.into_iter().collect();
    let n = pre.parse::<u64>()
        .expect("Error parsing integer at cli_helpers.rs:48");
    //let n = u64::from_str_radix(num_slice, 10)
    match size_tag {
        b"G" => n * 1073741824,
        b"M" => n * 1048576,
        b"k" => n * 1024,
        b"b" => n,
        _ => exit(0x0f01),
    }
}

named!(digit_char<&[u8], char>,
    alt!(
        char!('1') |
        char!('2') |
        char!('3') |
        char!('4') |
        char!('5') |
        char!('6') |
        char!('7') |
        char!('8') |
        char!('9') |
        char!('0')
    )
);

named!(get_threshhold<&[u8],u64>,
    do_parse!(
        nums:     many1!(digit_char) >>
        size_tag: alt!(tag!("M") | tag!("G") | tag!("k") | tag!("b")) >>
        (to_u64(nums, size_tag))
    )
);
<commit_msg>bugfix<commit_after>extern crate regex;

use colored::*;
use nom::IResult;
use std::process::exit;
use std::path::PathBuf;
use regex::Regex;
use error::check_regex;

pub fn get_excludes(cli_excludes: Option<&str>) -> Regex {
    //lazy_static! { // TODO fix this with an enum?
    let regex_git: Regex =
        Regex::new(r"\.git")
        .unwrap();
    //}
    match cli_excludes {
        Some(x) => { x.to_owned().push_str(r"|\.git"); check_regex(x) }
        _ => regex_git,

    }
}

pub fn get_depth(depth_from_cli: Option<&str>) -> u8 {
    if let Some(n) = depth_from_cli {
        n.parse::<u8>().expect("Please enter a positive whole number")
    }
    else {
        2
    }
}

pub fn get_num(num_from_cli: Option<&str>) -> usize {
    if let Some(num) = num_from_cli {
        num.parse::<usize>().expect("Please enter a positive whole number")
    }
    else {
        20
    }
}

pub fn get_dir(path_from_cli: Option<&str>) -> PathBuf {
    if let Some(read) = path_from_cli {
        PathBuf::from(read)
    }
    else {
        PathBuf::from(".")
    }
}

pub fn threshhold(s: Option<&str>) -> Option<u64> {
    s.map(pre_threshhold)
}

fn pre_threshhold(t_from_cli: &str) -> u64 {
    match get_threshhold(t_from_cli.as_bytes()) {
            IResult::Done(_,n) => n,
            _ => { eprintln!("{}: failed to parse threshhold. defaulting to 1M", "Warning".yellow()) ; 1048576 },
            }
}

fn to_u64(nums: Vec<char>, size_tag: &[u8]) -> u64 {
    let pre: String = nums.into_iter().collect();
    let n = pre.parse::<u64>()
        .expect("Error parsing integer at cli_helpers.rs:48");
    //let n = u64::from_str_radix(num_slice, 10)
    match size_tag {
        b"G" => n * 1073741824,
        b"M" => n * 1048576,
        b"k" => n * 1024,
        b"b" => n,
        _ => exit(0x0f01),
    }
}

named!(digit_char<&[u8], char>,
    alt!(
        char!('1') |
        char!('2') |
        char!('3') |
        char!('4') |
        char!('5') |
        char!('6') |
        char!('7') |
        char!('8') |
        char!('9') |
        char!('0')
    )
);

named!(get_threshhold<&[u8],u64>,
    do_parse!(
        nums:     many1!(digit_char) >>
        size_tag: alt!(tag!("M") | tag!("G") | tag!("k") | tag!("b")) >>
        (to_u64(nums, size_tag))
    )
);
<|endoftext|>


<commit_before>/// # Multiples of 3 and 5
/// ## Problem 1
/// If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
/// Find the sum of all the multiples of 3 or 5 below 1000.

fn main() {
    let mut total = 0;

    for n in range(1, 1000) {
        match n {
            v if v % 3 == 0 => total += v,
            v if v % 5 == 0 => total += v,
            _ => { /* do nothing */ }
        }
    }

    println!("result is {}", total);

}
<commit_msg>Rust: Problem 1 - refactor to use filter & sum<commit_after>/// # Multiples of 3 and 5
/// ## Problem 1
/// If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
/// Find the sum of all the multiples of 3 or 5 below 1000.

use std::iter::AdditiveIterator;

fn main() {
    let total = range(1, 1000).filter(|n| n % 3 == 0 || n % 5 == 0).sum();
    println!("result is {}", total);
}
<|endoftext|>


<commit_before>/*
 * Simple web server in Rust.
 * For now, the pool_size argument is not used: there is at most only
 * one client at a time.
 */
extern mod std;
use std::getopts::*;
use std::net::tcp;
use std::net::ip;
use std::task;
use std::uv;

use StatusCodes::StatusCode;
mod StatusCodes {
   // HTTP Status code
   pub struct StatusCode(int);
   impl StatusCode {
      fn shortmsg(&self) -> &'static str {
        match **self {
            401 => "Not authorized",
            404 => "Not found",
            505 => "HTTP Version Not Supported",
            _   => {fail!(fmt!("shortmsg not implemented for error code %?", self));}
         }
      }
      fn longmsg(&self) -> &'static str {
         match **self {
            400 => "The request is not well-formatted",
            401 => "The file cannot be accessed",
            404 => "The requested resource could not be found",
            505 => "The server does not support the HTTP protocol version used in the request",
            _   => {fail!(fmt!("longmsg not implemented for error code %?", self));}
         }
      }
   }
}

// TODO: investigate core::unstable::Exclusive
type ConnectMsg = (tcp::TcpNewConnection, core::unstable::Exclusive<core::comm::Chan<Option<tcp::TcpErrData>>>);

fn print_usage(program: ~str) {
   fail!(fmt!("Usage: %s -p port -s pool_size -d web_dir", program));
}

// Another version to parse the arguments, which uses getopts
fn parse_arguments_with_getopts(args: ~[~str]) -> (uint, uint, ~str) {
   let opts = ~[
      optopt("p"),
      optopt("s"),
      optopt("d")
   ];
   let matches = match getopts(vec::tail(args), opts) {
      result::Ok(m) => { m }
      result::Err(f) => {fail!(fail_str(f));}
   };
   let port = uint::from_str(opt_str(&matches, "p")).get();
   let pool_size = uint::from_str(opt_str(&matches, "s")).get();
   let web_dir = opt_str(&matches, "d");

   if port == -1 || pool_size == -1 || web_dir == ~"" {
      io::println("Bad arguments values: ");
      print_usage(copy args[0]);
   }

   (port, pool_size, web_dir)
}

/* read the file path */
fn read_file(path: ~str) -> (~[u8], int) {
   let res = io::read_whole_file(&Path(path));
   if result::is_err(&res) {
      // XXX how to make the difference between file not found and not authorized?
      // In both cases res = Err(~"error opening file")
      (~[], 404)
   } else {
      (res.get(), 200)
   }
}

/*
 * Read an HTTP request and return a couple (line, code).
 * Block until a line has been read, in which case line is
 * the line that has been read and code=0, or return the line
 * that has been read so far and code=1 in case of error.
 */
fn read_request(socket: &tcp::TcpSocket) -> (~str, uint) {
   let mut req: ~str = ~"";
   loop {
      let result = socket.read(0u);
      if result.is_err() {
         return (req, 1);
      }

      req += str::from_bytes(result.get());
      if str::contains(req, "\n") {
         return (req, 0);
      }
   }
}

fn clienterror(socket: &tcp::TcpSocket, cause: &str, errorcode: StatusCodes::StatusCode) {
   let errnum = errorcode.to_str();
   let shortmsg = errorcode.shortmsg();
   let longmsg = errorcode.longmsg();
   debug!(fmt!("Client error: %s -> %s %s %s", cause, errnum, shortmsg, longmsg));

   let body = ~"<title>Simple Web Server Error</title>\r\n"
      + fmt!("%s: %s\r\n", errnum, shortmsg)
      + fmt!("
%s: %s
\r\n", longmsg, cause)
      + ~"
The Simple Web Server\r\n";
   let header = fmt!("HTTP/1.0 %s %s\r\n", errnum, shortmsg) + ~"Content-type: text/html\r\n" + fmt!("Content-length: %u\r\n\r\n", body.len());

   socket.write(header.to_bytes());
   socket.write(body.to_bytes());
}

fn get_file_type(filename: &str) -> ~str {
   if filename.ends_with(~".html") {
      ~"text/html"
   } else if filename.ends_with(~".gif") {
      ~"image/gif"
   } else if filename.ends_with(~".jpg") {
      ~"image/jpeg"
   } else {
      ~"text/plain"
   }
}

fn clientsuccess(socket: &tcp::TcpSocket, filename: &str, content: ~[u8]) {
   let filetype = get_file_type(filename);

   let header = ~"HTTP/1.0 200 OK\r\nServer: Simple Web Server\r\n"
      +fmt!("Content-length: %u\r\n", content.len())
      +fmt!("Content-type: %s\r\n\r\n", filetype);

   socket.write(header.to_bytes());
   socket.write(content);
}

fn handle_connection(port_endpoint: ~Port<ConnectMsg>, web_dir: ~str) {
   // this is the task which accepts new connections
   do task::spawn{
      loop {
         let (conn, kill_ch) = port_endpoint.recv();
         debug!("Going to accept a new connection");
         match tcp::accept(conn) {
            result::Err(err) => {
               debug!(fmt!("Connection error: %?", err));
               kill_ch.send(Some(err));
            },
            result::Ok(socket) => {
               let peer_addr: ~str = ip::format_addr(&socket.get_peer_addr());
               debug!(fmt!("Connection accepted from %s", peer_addr));

               let (request, code) = read_request(&socket);
               if code == 0 {
                  // FIXME: hacky
                  let mut words = ~[];
                  for str::each_word(request) |word| { words.push(word) }

                  if words.len() < 3 {
                     clienterror(&socket, request, StatusCode(400));
                  } else if words[0] != ~"GET" {
                     clienterror(&socket, request, StatusCode(501));
                  // TODO: 1.1
                  } else if words[2] != ~"HTTP/1.0" {
                     clienterror(&socket, request, StatusCode(505));
                  } else {
                     let (content, code) = read_file(web_dir + words[1]);
                     match code {
                        200 => {clientsuccess(&socket, copy words[1], content);},
                        404 => {clienterror(&socket, copy words[1], StatusCode(404));},
                        401 => {clienterror(&socket, copy words[1], StatusCode(401));},
                        _   => {fail!(fmt!("Unknown error code: %d", code));}
                     }
                  }
               }
            }
         }
      }
   }
}

fn main()  {
   let args : ~[~str] = os::args();
   if (args.len() != 7) {
      io::print("Bad length: ");
      print_usage(copy args[0]);
   }

   let (port, pool_size, web_dir) = parse_arguments_with_getopts(args);

   //to play with string concatenation
   io::println(~"Port: " + port.to_str());
   io::println(~"Pool size: " + pool_size.to_str());
   io::println(~"Web dir: " + web_dir);

   //Connection information will be transmitted using this Port and Chan
   let (port_endpoint, chan_endpoint): (Port<ConnectMsg>, Chan<ConnectMsg>) = stream();

   handle_connection(~port_endpoint, web_dir);

   let result = tcp::listen(ip::v4::parse_addr("0.0.0.0"), port, 100,
         &uv::global_loop::get(),
         |_|{
            io::println("Server is now listening!");
         },
         |conn, kill_chan| {
            // The connection must be accepted from another task or the server will block.
            chan_endpoint.send((conn, kill_chan));
         }
   );

   if result.is_err() {
      fail!(fmt!("Failed listen: %?", result.get_err()));
   }
}

<commit_msg>spawn a new task for each TCP socket returned by port.recv().<commit_after>/*
 * Simple web server in Rust.
 * For now, the pool_size argument is not used: there is at most only
 * one client at a time.
 */
extern mod std;
use std::getopts::*;
use std::net::tcp;
use std::net::ip;
use std::task;
use std::uv;

use StatusCodes::StatusCode;
mod StatusCodes {
   // HTTP Status code
   pub struct StatusCode(int);
   impl StatusCode {
      fn shortmsg(&self) -> &'static str {
        match **self {
            401 => "Not authorized",
            404 => "Not found",
            505 => "HTTP Version Not Supported",
            _   => {fail!(fmt!("shortmsg not implemented for error code %?", self));}
         }
      }
      fn longmsg(&self) -> &'static str {
         match **self {
            400 => "The request is not well-formatted",
            401 => "The file cannot be accessed",
            404 => "The requested resource could not be found",
            505 => "The server does not support the HTTP protocol version used in the request",
            _   => {fail!(fmt!("longmsg not implemented for error code %?", self));}
         }
      }
   }
}

// TODO: investigate core::unstable::Exclusive
type ConnectMsg = (tcp::TcpNewConnection, core::unstable::Exclusive<core::comm::Chan<Option<tcp::TcpErrData>>>);

fn print_usage(program: ~str) {
   fail!(fmt!("Usage: %s -p port -s pool_size -d web_dir", program));
}

// Another version to parse the arguments, which uses getopts
fn parse_arguments_with_getopts(args: ~[~str]) -> (uint, uint, ~str) {
   let opts = ~[
      optopt("p"),
      optopt("s"),
      optopt("d")
   ];
   let matches = match getopts(vec::tail(args), opts) {
      result::Ok(m) => { m }
      result::Err(f) => {fail!(fail_str(f));}
   };
   let port = uint::from_str(opt_str(&matches, "p")).get();
   let pool_size = uint::from_str(opt_str(&matches, "s")).get();
   let web_dir = opt_str(&matches, "d");

   if port == -1 || pool_size == -1 || web_dir == ~"" {
      io::println("Bad arguments values: ");
      print_usage(copy args[0]);
   }

   (port, pool_size, web_dir)
}

/* read the file path */
fn read_file(path: ~str) -> (~[u8], int) {
   let res = io::read_whole_file(&Path(path));
   if result::is_err(&res) {
      // XXX how to make the difference between file not found and not authorized?
      // In both cases res = Err(~"error opening file")
      (~[], 404)
   } else {
      (res.get(), 200)
   }
}

/*
 * Read an HTTP request and return a couple (line, code).
 * Block until a line has been read, in which case line is
 * the line that has been read and code=0, or return the line
 * that has been read so far and code=1 in case of error.
 */
fn read_request(socket: &tcp::TcpSocket) -> (~str, uint) {
   let mut req: ~str = ~"";
   loop {
      let result = socket.read(0u);
      if result.is_err() {
         return (req, 1);
      }

      req += str::from_bytes(result.get());
      if str::contains(req, "\n") {
         return (req, 0);
      }
   }
}

fn clienterror(socket: &tcp::TcpSocket, cause: &str, errorcode: StatusCodes::StatusCode) {
   let errnum = errorcode.to_str();
   let shortmsg = errorcode.shortmsg();
   let longmsg = errorcode.longmsg();
   debug!(fmt!("Client error: %s -> %s %s %s", cause, errnum, shortmsg, longmsg));

   let body = ~"<title>Simple Web Server Error</title>\r\n"
      + fmt!("%s: %s\r\n", errnum, shortmsg)
      + fmt!("
%s: %s
\r\n", longmsg, cause)
      + ~"
The Simple Web Server\r\n";
   let header = fmt!("HTTP/1.0 %s %s\r\n", errnum, shortmsg) + ~"Content-type: text/html\r\n" + fmt!("Content-length: %u\r\n\r\n", body.len());

   socket.write(header.to_bytes());
   socket.write(body.to_bytes());
}

fn get_file_type(filename: &str) -> ~str {
   if filename.ends_with(~".html") {
      ~"text/html"
   } else if filename.ends_with(~".gif") {
      ~"image/gif"
   } else if filename.ends_with(~".jpg") {
      ~"image/jpeg"
   } else {
      ~"text/plain"
   }
}

fn clientsuccess(socket: &tcp::TcpSocket, filename: &str, content: ~[u8]) {
   let filetype = get_file_type(filename);

   let header = ~"HTTP/1.0 200 OK\r\nServer: Simple Web Server\r\n"
      +fmt!("Content-length: %u\r\n", content.len())
      +fmt!("Content-type: %s\r\n\r\n", filetype);

   socket.write(header.to_bytes());
   socket.write(content);
}

fn handle_connection(port_endpoint: ~Port<ConnectMsg>, web_dir2: ~str) {
   // this is the task which accepts new connections
   do task::spawn{
      loop {
         let (conn, kill_ch) = port_endpoint.recv();
         let web_dir: ~str = copy web_dir2;
         do task::spawn {
            debug!("Going to accept a new connection");
            match tcp::accept(conn) {
               result::Err(err) => {
                  debug!(fmt!("Connection error: %?", err));
                  kill_ch.send(Some(err));
               },
               result::Ok(socket) => {
                  let peer_addr: ~str = ip::format_addr(&socket.get_peer_addr());
                  debug!(fmt!("Connection accepted from %s", peer_addr));

                  let (request, code) = read_request(&socket);
                  if code == 0 {
                     // FIXME: hacky
                     let mut words = ~[];
                     for str::each_word(request) |word| { words.push(word) }

                     if words.len() < 3 {
                        clienterror(&socket, request, StatusCode(400));
                     } else if words[0] != ~"GET" {
                        clienterror(&socket, request, StatusCode(501));
                     // TODO: 1.1
                     } else if words[2] != ~"HTTP/1.0" {
                        clienterror(&socket, request, StatusCode(505));
                     } else {
                        let (content, code) = read_file(web_dir + words[1]);
                        match code {
                           200 => {clientsuccess(&socket, copy words[1], content);},
                           404 => {clienterror(&socket, copy words[1], StatusCode(404));},
                           401 => {clienterror(&socket, copy words[1], StatusCode(401));},
                           _   => {fail!(fmt!("Unknown error code: %d", code));}
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

fn main()  {
   let args : ~[~str] = os::args();
   if (args.len() != 7) {
      io::print("Bad length: ");
      print_usage(copy args[0]);
   }

   let (port, pool_size, web_dir) = parse_arguments_with_getopts(args);

   //to play with string concatenation
   io::println(~"Port: " + port.to_str());
   io::println(~"Pool size: " + pool_size.to_str());
   io::println(~"Web dir: " + web_dir);

   //Connection information will be transmitted using this Port and Chan
   let (port_endpoint, chan_endpoint): (Port<ConnectMsg>, Chan<ConnectMsg>) = stream();

   handle_connection(~port_endpoint, web_dir);

   let result = tcp::listen(ip::v4::parse_addr("0.0.0.0"), port, 100,
         &uv::global_loop::get(),
         |_|{
            io::println("Server is now listening!");
         },
         |conn, kill_chan| {
            // The connection must be accepted from another task or the server will block.
            chan_endpoint.send((conn, kill_chan));
         }
   );

   if result.is_err() {
      fail!(fmt!("Failed listen: %?", result.get_err()));
   }
}

<|endoftext|>


<commit_before>//! JIT Compiler to generate code fragments in runtime.

#![feature(libc)]
extern crate libc;
extern crate llvm_sys;

#[macro_use]
mod macros;
mod buffer;
mod util;

use std::mem;
use std::ptr;

use llvm_sys::core;
use llvm_sys::prelude::{
  LLVMContextRef,
  LLVMModuleRef
};
use llvm_sys::bit_reader::LLVMParseBitcodeInContext;
use llvm_sys::execution_engine::{
  LLVMExecutionEngineRef,
  LLVMLinkInMCJIT,
  LLVMMCJITCompilerOptions,
  LLVMCreateMCJITCompilerForModule
};
use llvm_sys::target::{
  LLVM_InitializeNativeTarget,
  LLVM_InitializeNativeAsmPrinter
};
use llvm_sys::target_machine::LLVMCodeModel;

use libc::{c_char, c_uint};

use buffer::MemoryBuffer;
use util::{str_to_chars, chars_to_str};

pub const JIT_OPT_LVEL: usize = 2;

pub struct JitCompiler {
  ctx   : LLVMContextRef,
  module: LLVMModuleRef,
  engine: LLVMExecutionEngineRef
}

fn new_module_from_bc(ctx: LLVMContextRef, path: &str) -> Result<LLVMModuleRef, String>
{
  unsafe {
    let mut out: LLVMModuleRef = mem::uninitialized();
    let mut err: *mut c_char   = mem::uninitialized();
    let buf = try!(MemoryBuffer::from_file(path));

    let ret = LLVMParseBitcodeInContext(ctx,
                                        buf.as_ptr(),
    	                                  &mut out,
    	                                  &mut err);
    llvm_ret!(ret, out, err)
  }
}

fn new_jit_ee(m: LLVMModuleRef, opt_lv: usize) -> Result<LLVMExecutionEngineRef, String>
{
  unsafe {
    let mut ee : LLVMExecutionEngineRef = mem::uninitialized();
    let mut err: *mut c_char = mem::uninitialized();

    LLVMLinkInMCJIT();
    expect_noerr!(LLVM_InitializeNativeTarget(), "failed to initialize native target");
    expect_noerr!(LLVM_InitializeNativeAsmPrinter(), "failed to initialize native asm printer");

    let mut opts = new_mcjit_compiler_options(opt_lv);
    let opts_size = mem::size_of::<LLVMMCJITCompilerOptions>();

    let ret = LLVMCreateMCJITCompilerForModule(&mut ee,
      		                                     m,
      		                                     &mut opts,
      		                                     opts_size as u64,
      		                                     &mut err);
    llvm_ret!(ret, ee, err)
  }
}

fn new_mcjit_compiler_options(opt_lv: usize) -> LLVMMCJITCompilerOptions
{
  LLVMMCJITCompilerOptions {
    OptLevel: opt_lv as c_uint,
    CodeModel: LLVMCodeModel::LLVMCodeModelJITDefault,
    NoFramePointerElim: 0,
    EnableFastISel: 1,
    MCJMM: ptr::null_mut()
  }
}

impl JitCompiler {
  pub fn create(bitcode_path: &str) -> Result<JitCompiler, String>
  {
    let ctx    = unsafe { core::LLVMContextCreate() };
    let module = try!(new_module_from_bc(ctx, bitcode_path));
    let ee     = new_jit_ee(module, JIT_OPT_LVEL);

    Err("".to_string())
  }
}<commit_msg>Add simple unit test to create JitCompiler.<commit_after>//! JIT Compiler to generate code fragments in runtime.

#![feature(libc)]
extern crate libc;
extern crate llvm_sys;

#[macro_use]
mod macros;
mod buffer;
mod util;

use std::mem;
use std::ptr;

use llvm_sys::core;
use llvm_sys::prelude::{
  LLVMContextRef,
  LLVMModuleRef
};
use llvm_sys::bit_reader::LLVMParseBitcodeInContext;
use llvm_sys::execution_engine::{
  LLVMExecutionEngineRef,
  LLVMLinkInMCJIT,
  LLVMMCJITCompilerOptions,
  LLVMCreateMCJITCompilerForModule
};
use llvm_sys::target::{
  LLVM_InitializeNativeTarget,
  LLVM_InitializeNativeAsmPrinter
};
use llvm_sys::target_machine::LLVMCodeModel;

use libc::{c_char, c_uint};

use buffer::MemoryBuffer;
use util::{str_to_chars, chars_to_str};

pub const JIT_OPT_LVEL: usize = 2;

pub struct JitCompiler {
  ctx   : LLVMContextRef,
  module: LLVMModuleRef,
  engine: LLVMExecutionEngineRef
}

fn new_module_from_bc(ctx: LLVMContextRef, path: &str) -> Result<LLVMModuleRef, String>
{
  unsafe {
    let mut out: LLVMModuleRef = mem::uninitialized();
    let mut err: *mut c_char   = mem::uninitialized();
    let buf = try!(MemoryBuffer::from_file(path));

    let ret = LLVMParseBitcodeInContext(ctx,
                                        buf.as_ptr(),
    	                                  &mut out,
    	                                  &mut err);
    llvm_ret!(ret, out, err)
  }
}

fn new_jit_ee(m: LLVMModuleRef, opt_lv: usize) -> Result<LLVMExecutionEngineRef, String>
{
  unsafe {
    let mut ee : LLVMExecutionEngineRef = mem::uninitialized();
    let mut err: *mut c_char = mem::uninitialized();

    LLVMLinkInMCJIT();
    expect_noerr!(LLVM_InitializeNativeTarget(), "failed to initialize native target");
    expect_noerr!(LLVM_InitializeNativeAsmPrinter(), "failed to initialize native asm printer");

    let mut opts = new_mcjit_compiler_options(opt_lv);
    let opts_size = mem::size_of::<LLVMMCJITCompilerOptions>();

    let ret = LLVMCreateMCJITCompilerForModule(&mut ee,
      		                                     m,
      		                                     &mut opts,
      		                                     opts_size as u64,
      		                                     &mut err);
    llvm_ret!(ret, ee, err)
  }
}

fn new_mcjit_compiler_options(opt_lv: usize) -> LLVMMCJITCompilerOptions
{
  LLVMMCJITCompilerOptions {
    OptLevel: opt_lv as c_uint,
    CodeModel: LLVMCodeModel::LLVMCodeModelJITDefault,
    NoFramePointerElim: 0,
    EnableFastISel: 1,
    MCJMM: ptr::null_mut()
  }
}

impl JitCompiler {
  pub fn new(bitcode_path: &str) -> Result<JitCompiler, String>
  {
    let ctx    = unsafe { core::LLVMContextCreate() };
    let module = try!(new_module_from_bc(ctx, bitcode_path));
    let ee     = new_jit_ee(module, JIT_OPT_LVEL);

    Err("".to_string())
  }
}

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

  #[test]
  fn test_ctor() {
    let jit = JitCompiler::new("target/test-ir/test-module.bc").ok().unwrap();
  }
}<|endoftext|>


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

use std::marker::PhantomData;
use std::collections::HashMap;
use std::any::Any;
use std::sync::{Arc, Mutex};
use std::fs;
use std::io::{BufWriter, Write, BufRead, BufReader};
use log;

use ui;
use render;
use format::{Component, TextComponent, Color};

pub struct CVar<T: Sized + Any + 'static> {
    pub name: &'static str,
    pub ty: PhantomData<T>,
    pub description: &'static str,
    pub mutable: bool,
    pub serializable: bool,
    pub default: &'static Fn() -> T,
}

impl Var for CVar<String> {
    fn serialize(&self, val: &Box<Any>) -> String {
        format!("\"{}\"", val.downcast_ref::<String>().unwrap())
    }

    fn deserialize(&self, input: &String) -> Box<Any> {
        Box::new((&input[1..input.len() - 1]).to_owned())
    }

    fn description(&self) -> &'static str {
        self.description
    }
    fn can_serialize(&self) -> bool {
        self.serializable
    }
}

pub trait Var {
    fn serialize(&self, val: &Box<Any>) -> String;
    fn deserialize(&self, input: &String) -> Box<Any>;
    fn description(&self) -> &'static str;
    fn can_serialize(&self) -> bool;
}

pub struct Console {
    names: HashMap<String, &'static str>,
    vars: HashMap<&'static str, Box<Var>>,
    var_values: HashMap<&'static str, Box<Any>>,

    history: Vec<Component>,

    collection: ui::Collection,
    active: bool,
    position: f64,
}

impl Console {
    pub fn new() -> Console {
        Console {
            names: HashMap::new(),
            vars: HashMap::new(),
            var_values: HashMap::new(),

            history: vec![Component::Text(TextComponent::new("")); 200],

            collection: ui::Collection::new(),
            active: false,
            position: -220.0,
        }
    }

    pub fn register<T: Sized + Any>(&mut self, var: CVar<T>)
        where CVar<T>: Var
    {
        if self.vars.contains_key(var.name) {
            panic!("Key registered twice {}", var.name);
        }
        self.names.insert(var.name.to_owned(), var.name);
        self.var_values.insert(var.name, Box::new((var.default)()));
        self.vars.insert(var.name, Box::new(var));
    }

    pub fn get<T: Sized + Any>(&self, var: CVar<T>) -> &T
        where CVar<T>: Var
    {
        // Should never fail
        self.var_values.get(var.name).unwrap().downcast_ref::<T>().unwrap()
    }

    pub fn set<T: Sized + Any>(&mut self, var: CVar<T>, val: T)
        where CVar<T>: Var
    {
        self.var_values.insert(var.name, Box::new(val));
        self.save_config();
    }

    pub fn is_active(&self) -> bool {
        self.active
    }

    pub fn toggle(&mut self) {
        self.active = !self.active;
    }

    pub fn tick(&mut self,
                ui_container: &mut ui::Container,
                renderer: &mut render::Renderer,
                delta: f64,
                width: f64) {
        // To make sure the console is always on top it constant removes and readds itself.
        // Its hacky but the console should never appear for normal users so its not really
        // a major issue.
        self.collection.remove_all(ui_container);
        if !self.active && self.position <= -220.0 {
            return;
        }
        if self.active {
            if self.position < 0.0 {
                self.position += delta * 4.0;
            } else {
                self.position = 0.0;
            }
        } else {
            if self.position > -220.0 {
                self.position -= delta * 4.0;
            } else {
                self.position = -220.0;
            }
        }
        let w = match ui_container.mode {
            ui::Mode::Scaled => width,
            ui::Mode::Unscaled(scale) => 854.0 / scale,
        };

        let mut background =
            ui::Image::new(render::Renderer::get_texture(renderer.get_textures_ref(),
                                                         "steven:solid"),
                           0.0,
                           self.position,
                           w,
                           220.0,
                           0.0,
                           0.0,
                           1.0,
                           1.0,
                           0,
                           0,
                           0);
        background.set_a(180);
        let background = self.collection.add(ui_container.add(background));

        let mut lines = Vec::new();
        let mut offset = 0.0;
        for line in self.history.iter().rev() {
            if offset >= 210.0 {
                break;
            }
            let mut fmt = ui::Formatted::with_width_limit(renderer,
                                                          line.clone(),
                                                          5.0,
                                                          5.0 + offset,
                                                          w - 1.0);
            fmt.set_parent(&background);
            fmt.set_v_attach(ui::VAttach::Bottom);
            offset += fmt.get_height();
            lines.push(ui_container.add(fmt));
        }
        for fmt in lines {
            self.collection.add(fmt);
        }
    }

    pub fn load_config(&mut self) {
        if let Ok(file) = fs::File::open("conf.cfg") {
            let reader = BufReader::new(file);
            for line in reader.lines() {
                let line = line.unwrap();
                if line.starts_with("#") || line.is_empty() {
                    continue;
                }
                let parts = line.splitn(2, ' ').map(|v| v.to_owned()).collect::<Vec<String>>();
                let (name, arg) = (&parts[0], &parts[1]);
                if let Some(var_name) = self.names.get(name) {
                    let var = self.vars.get(var_name).unwrap();
                    let val = var.deserialize(&arg);
                    if var.can_serialize() {
                        self.var_values.insert(var_name, val);
                    }
                } else {
                    println!("Missing prop");
                }
            }
        }
    }

    pub fn save_config(&self) {
        let mut file = BufWriter::new(fs::File::create("conf.cfg").unwrap());
        for (name, var) in &self.vars {
            if !var.can_serialize() {
                continue;
            }
            for line in var.description().lines() {
                write!(file, "# {}\n", line).unwrap();
            }
            write!(file,
                   "{} {}\n\n",
                   name,
                   var.serialize(self.var_values.get(name).unwrap()))
                .unwrap();
        }
    }

    fn log(&mut self, record: &log::LogRecord) {
        let mut file = &record.location().file().replace("\\", "/")[..];
        if let Some(pos) = file.rfind("src/") {
            file = &file[pos + 4..];
        }
        println!("[{}:{}][{}] {}",
                 file,
                 record.location().line(),
                 record.level(),
                 record.args());
        self.history.remove(0);
        let mut msg = TextComponent::new("");
        msg.modifier.extra = Some(vec![
            Component::Text(TextComponent::new("[")),
            {
                let mut msg = TextComponent::new(file);
                msg.modifier.color = Some(Color::Green);
                Component::Text(msg)
            },
            Component::Text(TextComponent::new(":")),
            {
                let mut msg = TextComponent::new(&format!("{}", record.location().line()));
                msg.modifier.color = Some(Color::Aqua);
                Component::Text(msg)
            },
            Component::Text(TextComponent::new("]")),
            Component::Text(TextComponent::new("[")),
            {
                let mut msg = TextComponent::new(&format!("{}", record.level()));
                msg.modifier.color = Some(match record.level() {
                    log::LogLevel::Debug => Color::Green,
                    log::LogLevel::Error => Color::Red,
                    log::LogLevel::Warn => Color::Yellow,
                    log::LogLevel::Info => Color::Aqua,
                    log::LogLevel::Trace => Color::Blue,
                });
                Component::Text(msg)
            },
            Component::Text(TextComponent::new("] ")),
            Component::Text(TextComponent::new(&format!("{}", record.args())))
        ]);
        self.history.push(Component::Text(msg));
    }
}

pub struct ConsoleProxy {
    console: Arc<Mutex<Console>>,
}

impl ConsoleProxy {
    pub fn new(con: Arc<Mutex<Console>>) -> ConsoleProxy {
        ConsoleProxy { console: con }
    }
}

impl log::Log for ConsoleProxy {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Trace
    }

    fn log(&self, record: &log::LogRecord) {
        if self.enabled(record.metadata()) {
            self.console.lock().unwrap().log(record);
        }
    }
}

unsafe impl Send for ConsoleProxy {}
unsafe impl Sync for ConsoleProxy {}
<commit_msg>Filter hyper and mime from logs<commit_after>// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::marker::PhantomData;
use std::collections::HashMap;
use std::any::Any;
use std::sync::{Arc, Mutex};
use std::fs;
use std::io::{BufWriter, Write, BufRead, BufReader};
use log;

use ui;
use render;
use format::{Component, TextComponent, Color};

const FILTERED_CRATES: &'static [&'static str] = &[
    "hyper",
    "mime",
];

pub struct CVar<T: Sized + Any + 'static> {
    pub name: &'static str,
    pub ty: PhantomData<T>,
    pub description: &'static str,
    pub mutable: bool,
    pub serializable: bool,
    pub default: &'static Fn() -> T,
}

impl Var for CVar<String> {
    fn serialize(&self, val: &Box<Any>) -> String {
        format!("\"{}\"", val.downcast_ref::<String>().unwrap())
    }

    fn deserialize(&self, input: &String) -> Box<Any> {
        Box::new((&input[1..input.len() - 1]).to_owned())
    }

    fn description(&self) -> &'static str {
        self.description
    }
    fn can_serialize(&self) -> bool {
        self.serializable
    }
}

pub trait Var {
    fn serialize(&self, val: &Box<Any>) -> String;
    fn deserialize(&self, input: &String) -> Box<Any>;
    fn description(&self) -> &'static str;
    fn can_serialize(&self) -> bool;
}

pub struct Console {
    names: HashMap<String, &'static str>,
    vars: HashMap<&'static str, Box<Var>>,
    var_values: HashMap<&'static str, Box<Any>>,

    history: Vec<Component>,

    collection: ui::Collection,
    active: bool,
    position: f64,
}

impl Console {
    pub fn new() -> Console {
        Console {
            names: HashMap::new(),
            vars: HashMap::new(),
            var_values: HashMap::new(),

            history: vec![Component::Text(TextComponent::new("")); 200],

            collection: ui::Collection::new(),
            active: false,
            position: -220.0,
        }
    }

    pub fn register<T: Sized + Any>(&mut self, var: CVar<T>)
        where CVar<T>: Var
    {
        if self.vars.contains_key(var.name) {
            panic!("Key registered twice {}", var.name);
        }
        self.names.insert(var.name.to_owned(), var.name);
        self.var_values.insert(var.name, Box::new((var.default)()));
        self.vars.insert(var.name, Box::new(var));
    }

    pub fn get<T: Sized + Any>(&self, var: CVar<T>) -> &T
        where CVar<T>: Var
    {
        // Should never fail
        self.var_values.get(var.name).unwrap().downcast_ref::<T>().unwrap()
    }

    pub fn set<T: Sized + Any>(&mut self, var: CVar<T>, val: T)
        where CVar<T>: Var
    {
        self.var_values.insert(var.name, Box::new(val));
        self.save_config();
    }

    pub fn is_active(&self) -> bool {
        self.active
    }

    pub fn toggle(&mut self) {
        self.active = !self.active;
    }

    pub fn tick(&mut self,
                ui_container: &mut ui::Container,
                renderer: &mut render::Renderer,
                delta: f64,
                width: f64) {
        // To make sure the console is always on top it constant removes and readds itself.
        // Its hacky but the console should never appear for normal users so its not really
        // a major issue.
        self.collection.remove_all(ui_container);
        if !self.active && self.position <= -220.0 {
            return;
        }
        if self.active {
            if self.position < 0.0 {
                self.position += delta * 4.0;
            } else {
                self.position = 0.0;
            }
        } else {
            if self.position > -220.0 {
                self.position -= delta * 4.0;
            } else {
                self.position = -220.0;
            }
        }
        let w = match ui_container.mode {
            ui::Mode::Scaled => width,
            ui::Mode::Unscaled(scale) => 854.0 / scale,
        };

        let mut background =
            ui::Image::new(render::Renderer::get_texture(renderer.get_textures_ref(),
                                                         "steven:solid"),
                           0.0,
                           self.position,
                           w,
                           220.0,
                           0.0,
                           0.0,
                           1.0,
                           1.0,
                           0,
                           0,
                           0);
        background.set_a(180);
        let background = self.collection.add(ui_container.add(background));

        let mut lines = Vec::new();
        let mut offset = 0.0;
        for line in self.history.iter().rev() {
            if offset >= 210.0 {
                break;
            }
            let mut fmt = ui::Formatted::with_width_limit(renderer,
                                                          line.clone(),
                                                          5.0,
                                                          5.0 + offset,
                                                          w - 1.0);
            fmt.set_parent(&background);
            fmt.set_v_attach(ui::VAttach::Bottom);
            offset += fmt.get_height();
            lines.push(ui_container.add(fmt));
        }
        for fmt in lines {
            self.collection.add(fmt);
        }
    }

    pub fn load_config(&mut self) {
        if let Ok(file) = fs::File::open("conf.cfg") {
            let reader = BufReader::new(file);
            for line in reader.lines() {
                let line = line.unwrap();
                if line.starts_with("#") || line.is_empty() {
                    continue;
                }
                let parts = line.splitn(2, ' ').map(|v| v.to_owned()).collect::<Vec<String>>();
                let (name, arg) = (&parts[0], &parts[1]);
                if let Some(var_name) = self.names.get(name) {
                    let var = self.vars.get(var_name).unwrap();
                    let val = var.deserialize(&arg);
                    if var.can_serialize() {
                        self.var_values.insert(var_name, val);
                    }
                } else {
                    println!("Missing prop");
                }
            }
        }
    }

    pub fn save_config(&self) {
        let mut file = BufWriter::new(fs::File::create("conf.cfg").unwrap());
        for (name, var) in &self.vars {
            if !var.can_serialize() {
                continue;
            }
            for line in var.description().lines() {
                write!(file, "# {}\n", line).unwrap();
            }
            write!(file,
                   "{} {}\n\n",
                   name,
                   var.serialize(self.var_values.get(name).unwrap()))
                .unwrap();
        }
    }

    fn log(&mut self, record: &log::LogRecord) {
        for filtered in FILTERED_CRATES {
            if record.location().module_path().starts_with(filtered) {
                return;
            }
        }

        let mut file = &record.location().file().replace("\\", "/")[..];
        if let Some(pos) = file.rfind("src/") {
            file = &file[pos + 4..];
        }

        println!("[{}:{}][{}] {}",
                 file,
                 record.location().line(),
                 record.level(),
                 record.args());
        self.history.remove(0);
        let mut msg = TextComponent::new("");
        msg.modifier.extra = Some(vec![
            Component::Text(TextComponent::new("[")),
            {
                let mut msg = TextComponent::new(file);
                msg.modifier.color = Some(Color::Green);
                Component::Text(msg)
            },
            Component::Text(TextComponent::new(":")),
            {
                let mut msg = TextComponent::new(&format!("{}", record.location().line()));
                msg.modifier.color = Some(Color::Aqua);
                Component::Text(msg)
            },
            Component::Text(TextComponent::new("]")),
            Component::Text(TextComponent::new("[")),
            {
                let mut msg = TextComponent::new(&format!("{}", record.level()));
                msg.modifier.color = Some(match record.level() {
                    log::LogLevel::Debug => Color::Green,
                    log::LogLevel::Error => Color::Red,
                    log::LogLevel::Warn => Color::Yellow,
                    log::LogLevel::Info => Color::Aqua,
                    log::LogLevel::Trace => Color::Blue,
                });
                Component::Text(msg)
            },
            Component::Text(TextComponent::new("] ")),
            Component::Text(TextComponent::new(&format!("{}", record.args())))
        ]);
        self.history.push(Component::Text(msg));
    }
}

pub struct ConsoleProxy {
    console: Arc<Mutex<Console>>,
}

impl ConsoleProxy {
    pub fn new(con: Arc<Mutex<Console>>) -> ConsoleProxy {
        ConsoleProxy { console: con }
    }
}

impl log::Log for ConsoleProxy {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Trace
    }

    fn log(&self, record: &log::LogRecord) {
        if self.enabled(record.metadata()) {
            self.console.lock().unwrap().log(record);
        }
    }
}

unsafe impl Send for ConsoleProxy {}
unsafe impl Sync for ConsoleProxy {}
<|endoftext|>