<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t;
#[cfg(not(windows))]
use uucore::mode;
use uucore::InvalidEncodingHandling;
use walkdir::WalkDir;

static ABOUT: &str = "Change the mode of each FILE to MODE.
 With --reference, change the mode of each FILE to that of RFILE.";

mod options {
    pub const CHANGES: &str = "changes";
    pub const QUIET: &str = "quiet"; // visible_alias("silent")
    pub const VERBOSE: &str = "verbose";
    pub const NO_PRESERVE_ROOT: &str = "no-preserve-root";
    pub const PRESERVE_ROOT: &str = "preserve-root";
    pub const REFERENCE: &str = "RFILE";
    pub const RECURSIVE: &str = "recursive";
    pub const MODE: &str = "MODE";
    pub const FILE: &str = "FILE";
}

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... MODE[,MODE]... FILE...
or: {0} [OPTION]... OCTAL-MODE FILE...
or: {0} [OPTION]... --reference=RFILE FILE...",
        executable!()
    )
}

fn get_long_usage() -> String {
    String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.")
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let mut args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    // Before we can parse 'args' with clap (and previously getopts),
    // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
    let mode_had_minus_prefix = strip_minus_from_mode(&mut args);

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::CHANGES)
                .long(options::CHANGES)
                .short("c")
                .help("like verbose but report only when a change is made"),
        )
        .arg(
            Arg::with_name(options::QUIET)
                .long(options::QUIET)
                .visible_alias("silent")
                .short("f")
                .help("suppress most error messages"),
        )
        .arg(
            Arg::with_name(options::VERBOSE)
                .long(options::VERBOSE)
                .short("v")
                .help("output a diagnostic for every file processed"),
        )
        .arg(
            Arg::with_name(options::NO_PRESERVE_ROOT)
                .long(options::NO_PRESERVE_ROOT)
                .help("do not treat '/' specially (the default)"),
        )
        .arg(
            Arg::with_name(options::PRESERVE_ROOT)
                .long(options::PRESERVE_ROOT)
                .help("fail to operate recursively on '/'"),
        )
        .arg(
            Arg::with_name(options::RECURSIVE)
                .long(options::RECURSIVE)
                .short("R")
                .help("change files and directories recursively"),
        )
        .arg(
            Arg::with_name(options::REFERENCE)
                .long("reference")
                .takes_value(true)
                .help("use RFILE's mode instead of MODE values"),
        )
        .arg(
            Arg::with_name(options::MODE)
                .required_unless(options::REFERENCE)
                .takes_value(true),
            // It would be nice if clap could parse with delimiter, e.g. "g-x,u+x",
            // however .multiple(true) cannot be used here because FILE already needs that.
            // Only one positional argument with .multiple(true) set is allowed per command
        )
        .arg(
            Arg::with_name(options::FILE)
                .required_unless(options::MODE)
                .multiple(true),
        )
        .get_matches_from(args);

    let changes = matches.is_present(options::CHANGES);
    let quiet = matches.is_present(options::QUIET);
    let verbose = matches.is_present(options::VERBOSE);
    let preserve_root = matches.is_present(options::PRESERVE_ROOT);
    let recursive = matches.is_present(options::RECURSIVE);
    let fmode =
        matches
            .value_of(options::REFERENCE)
            .and_then(|ref fref| match fs::metadata(fref) {
                Ok(meta) => Some(meta.mode()),
                Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
            });
    let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
    let mut cmode = if mode_had_minus_prefix {
        // clap parsing is finished, now put prefix back
        Some(format!("-{}", modes))
    } else {
        Some(modes.to_string())
    };
    let mut files: Vec<String> = matches
        .values_of(options::FILE)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();
    if fmode.is_some() {
        // "--reference" and MODE are mutually exclusive
        // if "--reference" was used MODE needs to be interpreted as another FILE
        // it wasn't possible to implement this behavior directly with clap
        files.push(cmode.unwrap());
        cmode = None;
    }

    let chmoder = Chmoder {
        changes,
        quiet,
        verbose,
        preserve_root,
        recursive,
        fmode,
        cmode,
    };
    match chmoder.chmod(files) {
        Ok(()) => {}
        Err(e) => return e,
    }

    0
}

// Iterate 'args' and delete the first occurrence
// of a prefix '-' if it's associated with MODE
// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE"
pub fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
    for arg in args {
        if arg.starts_with('-') {
            if let Some(second) = arg.chars().nth(1) {
                match second {
                    'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => {
                        // TODO: use strip_prefix() once minimum rust version reaches 1.45.0
                        *arg = arg[1..arg.len()].to_string();
                        return true;
                    }
                    _ => {}
                }
            }
        }
    }
    false
}

struct Chmoder {
    changes: bool,
    quiet: bool,
    verbose: bool,
    preserve_root: bool,
    recursive: bool,
    fmode: Option<u32>,
    cmode: Option<String>,
}

impl Chmoder {
    fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
        let mut r = Ok(());

        for filename in &files {
            let filename = &filename[..];
            let file = Path::new(filename);
            if !file.exists() {
                if is_symlink(file) {
                    println!(
                        "failed to change mode of '{}' from 0000 (---------) to 0000 (---------)",
                        filename
                    );
                    if !self.quiet {
                        show_error!("cannot operate on dangling symlink '{}'", filename);
                    }
                } else {
                    show_error!("cannot access '{}': No such file or directory", filename);
                }
                return Err(1);
            }
            if self.recursive && self.preserve_root && filename == "/" {
                show_error!(
                    "it is dangerous to operate recursively on '{}'\nuse --no-preserve-root to override this failsafe",
                    filename
                );
                return Err(1);
            }
            if !self.recursive {
                r = self.chmod_file(&file).and(r);
            } else {
                for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) {
                    let file = entry.path();
                    r = self.chmod_file(&file).and(r);
                }
            }
        }
        r
    }

    #[cfg(windows)]
    fn chmod_file(&self, file: &Path) -> Result<(), i32> {
        // chmod is useless on Windows
        // it doesn't set any permissions at all
        // instead it just sets the readonly attribute on the file
        Err(0)
    }
    #[cfg(any(unix, target_os = "redox"))]
    fn chmod_file(&self, file: &Path) -> Result<(), i32> {
        let mut fperm = match fs::metadata(file) {
            Ok(meta) => meta.mode() & 0o7777,
            Err(err) => {
                if is_symlink(file) {
                    if self.verbose {
                        println!(
                            "neither symbolic link '{}' nor referent has been changed",
                            file.display()
                        );
                    }
                    return Ok(());
                } else if err.kind() == std::io::ErrorKind::PermissionDenied {
                    show_error!("'{}': Permission denied", file.display());
                } else {
                    show_error!("'{}': {}", file.display(), err);
                }
                return Err(1);
            }
        };
        match self.fmode {
            Some(mode) => self.change_file(fperm, mode, file)?,
            None => {
                let cmode_unwrapped = self.cmode.clone().unwrap();
                for mode in cmode_unwrapped.split(',') {
                    // cmode is guaranteed to be Some in this case
                    let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                    let result = if mode.contains(arr) {
                        mode::parse_numeric(fperm, mode)
                    } else {
                        mode::parse_symbolic(fperm, mode, file.is_dir())
                    };
                    match result {
                        Ok(mode) => {
                            self.change_file(fperm, mode, file)?;
                            fperm = mode;
                        }
                        Err(f) => {
                            if !self.quiet {
                                show_error!("{}", f);
                            }
                            return Err(1);
                        }
                    }
                }
            }
        }

        Ok(())
    }

    #[cfg(unix)]
    fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> {
        if fperm == mode {
            if self.verbose && !self.changes {
                println!(
                    "mode of '{}' retained as {:04o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                );
            }
            Ok(())
        } else if let Err(err) = fs::set_permissions(file, fs::Permissions::from_mode(mode)) {
            if !self.quiet {
                show_error!("{}", err);
            }
            if self.verbose {
                show_error!(
                    "failed to change mode of file '{}' from {:o} ({}) to {:o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                    mode,
                    display_permissions_unix(mode as mode_t, false)
                );
            }
            Err(1)
        } else {
            if self.verbose || self.changes {
                show_error!(
                    "mode of '{}' changed from {:o} ({}) to {:o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                    mode,
                    display_permissions_unix(mode as mode_t, false)
                );
            }
            Ok(())
        }
    }
}

pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
    match fs::symlink_metadata(path) {
        Ok(m) => m.file_type().is_symlink(),
        Err(_) => false,
    }
}
<commit_msg>refactor/chmod ~ fix `cargo clippy` complaint (clippy::needless_borrow)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t;
#[cfg(not(windows))]
use uucore::mode;
use uucore::InvalidEncodingHandling;
use walkdir::WalkDir;

static ABOUT: &str = "Change the mode of each FILE to MODE.
 With --reference, change the mode of each FILE to that of RFILE.";

mod options {
    pub const CHANGES: &str = "changes";
    pub const QUIET: &str = "quiet"; // visible_alias("silent")
    pub const VERBOSE: &str = "verbose";
    pub const NO_PRESERVE_ROOT: &str = "no-preserve-root";
    pub const PRESERVE_ROOT: &str = "preserve-root";
    pub const REFERENCE: &str = "RFILE";
    pub const RECURSIVE: &str = "recursive";
    pub const MODE: &str = "MODE";
    pub const FILE: &str = "FILE";
}

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... MODE[,MODE]... FILE...
or: {0} [OPTION]... OCTAL-MODE FILE...
or: {0} [OPTION]... --reference=RFILE FILE...",
        executable!()
    )
}

fn get_long_usage() -> String {
    String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.")
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let mut args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    // Before we can parse 'args' with clap (and previously getopts),
    // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
    let mode_had_minus_prefix = strip_minus_from_mode(&mut args);

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::CHANGES)
                .long(options::CHANGES)
                .short("c")
                .help("like verbose but report only when a change is made"),
        )
        .arg(
            Arg::with_name(options::QUIET)
                .long(options::QUIET)
                .visible_alias("silent")
                .short("f")
                .help("suppress most error messages"),
        )
        .arg(
            Arg::with_name(options::VERBOSE)
                .long(options::VERBOSE)
                .short("v")
                .help("output a diagnostic for every file processed"),
        )
        .arg(
            Arg::with_name(options::NO_PRESERVE_ROOT)
                .long(options::NO_PRESERVE_ROOT)
                .help("do not treat '/' specially (the default)"),
        )
        .arg(
            Arg::with_name(options::PRESERVE_ROOT)
                .long(options::PRESERVE_ROOT)
                .help("fail to operate recursively on '/'"),
        )
        .arg(
            Arg::with_name(options::RECURSIVE)
                .long(options::RECURSIVE)
                .short("R")
                .help("change files and directories recursively"),
        )
        .arg(
            Arg::with_name(options::REFERENCE)
                .long("reference")
                .takes_value(true)
                .help("use RFILE's mode instead of MODE values"),
        )
        .arg(
            Arg::with_name(options::MODE)
                .required_unless(options::REFERENCE)
                .takes_value(true),
            // It would be nice if clap could parse with delimiter, e.g. "g-x,u+x",
            // however .multiple(true) cannot be used here because FILE already needs that.
            // Only one positional argument with .multiple(true) set is allowed per command
        )
        .arg(
            Arg::with_name(options::FILE)
                .required_unless(options::MODE)
                .multiple(true),
        )
        .get_matches_from(args);

    let changes = matches.is_present(options::CHANGES);
    let quiet = matches.is_present(options::QUIET);
    let verbose = matches.is_present(options::VERBOSE);
    let preserve_root = matches.is_present(options::PRESERVE_ROOT);
    let recursive = matches.is_present(options::RECURSIVE);
    let fmode = matches
        .value_of(options::REFERENCE)
        .and_then(|fref| match fs::metadata(fref) {
            Ok(meta) => Some(meta.mode()),
            Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
        });
    let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
    let mut cmode = if mode_had_minus_prefix {
        // clap parsing is finished, now put prefix back
        Some(format!("-{}", modes))
    } else {
        Some(modes.to_string())
    };
    let mut files: Vec<String> = matches
        .values_of(options::FILE)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();
    if fmode.is_some() {
        // "--reference" and MODE are mutually exclusive
        // if "--reference" was used MODE needs to be interpreted as another FILE
        // it wasn't possible to implement this behavior directly with clap
        files.push(cmode.unwrap());
        cmode = None;
    }

    let chmoder = Chmoder {
        changes,
        quiet,
        verbose,
        preserve_root,
        recursive,
        fmode,
        cmode,
    };
    match chmoder.chmod(files) {
        Ok(()) => {}
        Err(e) => return e,
    }

    0
}

// Iterate 'args' and delete the first occurrence
// of a prefix '-' if it's associated with MODE
// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE"
pub fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
    for arg in args {
        if arg.starts_with('-') {
            if let Some(second) = arg.chars().nth(1) {
                match second {
                    'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => {
                        // TODO: use strip_prefix() once minimum rust version reaches 1.45.0
                        *arg = arg[1..arg.len()].to_string();
                        return true;
                    }
                    _ => {}
                }
            }
        }
    }
    false
}

struct Chmoder {
    changes: bool,
    quiet: bool,
    verbose: bool,
    preserve_root: bool,
    recursive: bool,
    fmode: Option<u32>,
    cmode: Option<String>,
}

impl Chmoder {
    fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
        let mut r = Ok(());

        for filename in &files {
            let filename = &filename[..];
            let file = Path::new(filename);
            if !file.exists() {
                if is_symlink(file) {
                    println!(
                        "failed to change mode of '{}' from 0000 (---------) to 0000 (---------)",
                        filename
                    );
                    if !self.quiet {
                        show_error!("cannot operate on dangling symlink '{}'", filename);
                    }
                } else {
                    show_error!("cannot access '{}': No such file or directory", filename);
                }
                return Err(1);
            }
            if self.recursive && self.preserve_root && filename == "/" {
                show_error!(
                    "it is dangerous to operate recursively on '{}'\nuse --no-preserve-root to override this failsafe",
                    filename
                );
                return Err(1);
            }
            if !self.recursive {
                r = self.chmod_file(file).and(r);
            } else {
                for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) {
                    let file = entry.path();
                    r = self.chmod_file(file).and(r);
                }
            }
        }
        r
    }

    #[cfg(windows)]
    fn chmod_file(&self, file: &Path) -> Result<(), i32> {
        // chmod is useless on Windows
        // it doesn't set any permissions at all
        // instead it just sets the readonly attribute on the file
        Err(0)
    }
    #[cfg(any(unix, target_os = "redox"))]
    fn chmod_file(&self, file: &Path) -> Result<(), i32> {
        let mut fperm = match fs::metadata(file) {
            Ok(meta) => meta.mode() & 0o7777,
            Err(err) => {
                if is_symlink(file) {
                    if self.verbose {
                        println!(
                            "neither symbolic link '{}' nor referent has been changed",
                            file.display()
                        );
                    }
                    return Ok(());
                } else if err.kind() == std::io::ErrorKind::PermissionDenied {
                    show_error!("'{}': Permission denied", file.display());
                } else {
                    show_error!("'{}': {}", file.display(), err);
                }
                return Err(1);
            }
        };
        match self.fmode {
            Some(mode) => self.change_file(fperm, mode, file)?,
            None => {
                let cmode_unwrapped = self.cmode.clone().unwrap();
                for mode in cmode_unwrapped.split(',') {
                    // cmode is guaranteed to be Some in this case
                    let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                    let result = if mode.contains(arr) {
                        mode::parse_numeric(fperm, mode)
                    } else {
                        mode::parse_symbolic(fperm, mode, file.is_dir())
                    };
                    match result {
                        Ok(mode) => {
                            self.change_file(fperm, mode, file)?;
                            fperm = mode;
                        }
                        Err(f) => {
                            if !self.quiet {
                                show_error!("{}", f);
                            }
                            return Err(1);
                        }
                    }
                }
            }
        }

        Ok(())
    }

    #[cfg(unix)]
    fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> {
        if fperm == mode {
            if self.verbose && !self.changes {
                println!(
                    "mode of '{}' retained as {:04o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                );
            }
            Ok(())
        } else if let Err(err) = fs::set_permissions(file, fs::Permissions::from_mode(mode)) {
            if !self.quiet {
                show_error!("{}", err);
            }
            if self.verbose {
                show_error!(
                    "failed to change mode of file '{}' from {:o} ({}) to {:o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                    mode,
                    display_permissions_unix(mode as mode_t, false)
                );
            }
            Err(1)
        } else {
            if self.verbose || self.changes {
                show_error!(
                    "mode of '{}' changed from {:o} ({}) to {:o} ({})",
                    file.display(),
                    fperm,
                    display_permissions_unix(fperm as mode_t, false),
                    mode,
                    display_permissions_unix(mode as mode_t, false)
                );
            }
            Ok(())
        }
    }
}

pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
    match fs::symlink_metadata(path) {
        Ok(m) => m.file_type().is_symlink(),
        Err(_) => false,
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) delim

use clap::{crate_version, App, AppSettings, Arg};
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read, stdout, Write};
use std::path::Path;
use uucore::error::{FromIo, UResult};

static ABOUT: &str = "Write lines consisting of the sequentially corresponding lines from each
FILE, separated by TABs, to standard output.";

mod options {
    pub const DELIMITER: &str = "delimiters";
    pub const SERIAL: &str = "serial";
    pub const FILE: &str = "file";
}

// Wraps BufReader and stdin
fn read_line<R: Read>(
    reader: Option<&mut BufReader<R>>,
    buf: &mut String,
) -> std::io::Result<usize> {
    match reader {
        Some(reader) => reader.read_line(buf),
        None => stdin().read_line(buf),
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let matches = uu_app().get_matches_from(args);

    let serial = matches.is_present(options::SERIAL);
    let delimiters = matches.value_of(options::DELIMITER).unwrap();
    let files = matches
        .values_of(options::FILE)
        .unwrap()
        .map(|s| s.to_owned())
        .collect();
    paste(files, serial, delimiters)
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .setting(AppSettings::InferLongArgs)
        .arg(
            Arg::new(options::SERIAL)
                .long(options::SERIAL)
                .short('s')
                .help("paste one file at a time instead of in parallel"),
        )
        .arg(
            Arg::new(options::DELIMITER)
                .long(options::DELIMITER)
                .short('d')
                .help("reuse characters from LIST instead of TABs")
                .value_name("LIST")
                .default_value("\t")
                .hide_default_value(true),
        )
        .arg(
            Arg::new(options::FILE)
                .value_name("FILE")
                .multiple_occurrences(true)
                .default_value("-"),
        )
}

fn paste(filenames: Vec<String>, serial: bool, delimiters: &str) -> UResult<()> {
    let mut files = vec![];
    for name in filenames {
        let file = if name == "-" {
            None
        } else {
            let path = Path::new(&name);
            let r = File::open(path).map_err_context(String::new)?;
            Some(BufReader::new(r))
        };
        files.push(file);
    }

    let delimiters: Vec<char> = unescape(delimiters).chars().collect();
    let mut delim_count = 0;
    let stdout = stdout();
    let mut stdout = stdout.lock();

    let mut line = String::new();
    let mut output = String::new();
    if serial {
        for file in &mut files {
            output.clear();
            loop {
                line.clear();
                match read_line(file.as_mut(), &mut line) {
                    Ok(0) => break,
                    Ok(_) => {
                        output.push_str(line.trim_end());
                        output.push(delimiters[delim_count % delimiters.len()]);
                    }
                    Err(e) => return Err(e.map_err_context(String::new)),
                }
                delim_count += 1;
            }
            output.pop();
            writeln!(stdout, "{}", output)?;
        }
    } else {
        let mut eof = vec![false; files.len()];
        loop {
            output.clear();
            let mut eof_count = 0;
            for (i, file) in files.iter_mut().enumerate() {
                if eof[i] {
                    eof_count += 1;
                } else {
                    line.clear();
                    match read_line(file.as_mut(), &mut line) {
                        Ok(0) => {
                            eof[i] = true;
                            eof_count += 1;
                        }
                        Ok(_) => output.push_str(line.trim_end()),
                        Err(e) => return Err(e.map_err_context(String::new)),
                    }
                }
                output.push(delimiters[delim_count % delimiters.len()]);
                delim_count += 1;
            }
            if files.len() == eof_count {
                break;
            }
            // Remove final delimiter
            output.pop();
            writeln!(stdout, "{}", output)?;
            delim_count = 0;
        }
    }
    Ok(())
}

// Unescape all special characters
// TODO: this will need work to conform to GNU implementation
fn unescape(s: &str) -> String {
    s.replace("\\n", "\n")
        .replace("\\t", "\t")
        .replace("\\\\", "\\")
        .replace('\\', "")
}
<commit_msg>paste: Create vec with capacity<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) delim

use clap::{crate_version, App, AppSettings, Arg};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, Read, Write};
use std::path::Path;
use uucore::error::{FromIo, UResult};

static ABOUT: &str = "Write lines consisting of the sequentially corresponding lines from each
FILE, separated by TABs, to standard output.";

mod options {
    pub const DELIMITER: &str = "delimiters";
    pub const SERIAL: &str = "serial";
    pub const FILE: &str = "file";
}

// Wraps BufReader and stdin
fn read_line<R: Read>(
    reader: Option<&mut BufReader<R>>,
    buf: &mut String,
) -> std::io::Result<usize> {
    match reader {
        Some(reader) => reader.read_line(buf),
        None => stdin().read_line(buf),
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let matches = uu_app().get_matches_from(args);

    let serial = matches.is_present(options::SERIAL);
    let delimiters = matches.value_of(options::DELIMITER).unwrap();
    let files = matches
        .values_of(options::FILE)
        .unwrap()
        .map(|s| s.to_owned())
        .collect();
    paste(files, serial, delimiters)
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .setting(AppSettings::InferLongArgs)
        .arg(
            Arg::new(options::SERIAL)
                .long(options::SERIAL)
                .short('s')
                .help("paste one file at a time instead of in parallel"),
        )
        .arg(
            Arg::new(options::DELIMITER)
                .long(options::DELIMITER)
                .short('d')
                .help("reuse characters from LIST instead of TABs")
                .value_name("LIST")
                .default_value("\t")
                .hide_default_value(true),
        )
        .arg(
            Arg::new(options::FILE)
                .value_name("FILE")
                .multiple_occurrences(true)
                .default_value("-"),
        )
}

fn paste(filenames: Vec<String>, serial: bool, delimiters: &str) -> UResult<()> {
    let mut files = Vec::with_capacity(filenames.len());
    for name in filenames {
        let file = if name == "-" {
            None
        } else {
            let path = Path::new(&name);
            let r = File::open(path).map_err_context(String::new)?;
            Some(BufReader::new(r))
        };
        files.push(file);
    }

    let delimiters: Vec<char> = unescape(delimiters).chars().collect();
    let mut delim_count = 0;
    let stdout = stdout();
    let mut stdout = stdout.lock();

    let mut line = String::new();
    let mut output = String::new();
    if serial {
        for file in &mut files {
            output.clear();
            loop {
                line.clear();
                match read_line(file.as_mut(), &mut line) {
                    Ok(0) => break,
                    Ok(_) => {
                        output.push_str(line.trim_end());
                        output.push(delimiters[delim_count % delimiters.len()]);
                    }
                    Err(e) => return Err(e.map_err_context(String::new)),
                }
                delim_count += 1;
            }
            output.pop();
            writeln!(stdout, "{}", output)?;
        }
    } else {
        let mut eof = vec![false; files.len()];
        loop {
            output.clear();
            let mut eof_count = 0;
            for (i, file) in files.iter_mut().enumerate() {
                if eof[i] {
                    eof_count += 1;
                } else {
                    line.clear();
                    match read_line(file.as_mut(), &mut line) {
                        Ok(0) => {
                            eof[i] = true;
                            eof_count += 1;
                        }
                        Ok(_) => output.push_str(line.trim_end()),
                        Err(e) => return Err(e.map_err_context(String::new)),
                    }
                }
                output.push(delimiters[delim_count % delimiters.len()]);
                delim_count += 1;
            }
            if files.len() == eof_count {
                break;
            }
            // Remove final delimiter
            output.pop();
            writeln!(stdout, "{}", output)?;
            delim_count = 0;
        }
    }
    Ok(())
}

// Unescape all special characters
// TODO: this will need work to conform to GNU implementation
fn unescape(s: &str) -> String {
    s.replace("\\n", "\n")
        .replace("\\t", "\t")
        .replace("\\\\", "\\")
        .replace('\\', "")
}
<|endoftext|>


<commit_before>use std::boxed::Box;

use super::{CueSheetFormat, Format, MKVMergeTextFormat, MatroskaTocFormat, Reader, Writer};

pub struct Factory {}

impl Factory {
    pub fn get_extensions() -> Vec<(&'static str, Format)> {
        let mut result = Vec::<(&'static str, Format)>::new();

        result.push((MKVMergeTextFormat::get_extension(), Format::MKVMergeText));
        result.push((CueSheetFormat::get_extension(), Format::CueSheet));

        result
    }

    pub fn get_extension(format: &Format, is_audio_only: bool) -> &'static str {
        match *format {
            Format::CueSheet => CueSheetFormat::get_extension(),
            Format::Flac => "flac",
            Format::Matroska => {
                if !is_audio_only {
                    MatroskaTocFormat::get_extension()
                } else {
                    MatroskaTocFormat::get_audio_extension()
                }
            }
            Format::MKVMergeText => MKVMergeTextFormat::get_extension(),
            Format::MP3 => "mp3",
            Format::Opus => "opus",
            Format::Vorbis => "oga",
            Format::Wave => "wave",
        }
    }

    pub fn get_reader(format: &Format) -> Box<Reader> {
        match *format {
            Format::CueSheet => unimplemented!("Reader for toc::Format::CueSheet"),
            Format::Flac => unimplemented!("Reader for toc::Format::Flac"),
            Format::Matroska => unimplemented!("Reader for toc::Format::Matroska"),
            Format::MKVMergeText => MKVMergeTextFormat::new_as_boxed(),
            Format::MP3 => unimplemented!("Reader for toc::Format::MP3"),
            Format::Opus => unimplemented!("Reader for toc::Format::Opus"),
            Format::Vorbis => unimplemented!("Reader for toc::Format::Vorbis"),
            Format::Wave => unimplemented!("Reader for toc::Format::Wave"),
        }
    }

    pub fn get_writer(format: &Format) -> Box<Writer> {
        match *format {
            Format::CueSheet => CueSheetFormat::new_as_boxed(),
            Format::Flac => unimplemented!("Writer for toc::Format::Wave"),
            Format::Matroska => unimplemented!("Writer for toc::Format::Matroska"),
            Format::MKVMergeText => MKVMergeTextFormat::new_as_boxed(),
            Format::MP3 => unimplemented!("Writer for toc::Format::MP3"),
            Format::Opus => unimplemented!("Writer for toc::Format::Opus"),
            Format::Vorbis => unimplemented!("Writer for toc::Format::Vorbis"),
            Format::Wave => unimplemented!("Writer for toc::Format::Wave"),
        }
    }
}
<commit_msg>Don't panic when a cue sheet is available<commit_after>use std::boxed::Box;

use super::{CueSheetFormat, Format, MKVMergeTextFormat, MatroskaTocFormat, Reader, Writer};

pub struct Factory {}

impl Factory {
    pub fn get_extensions() -> Vec<(&'static str, Format)> {
        let mut result = Vec::<(&'static str, Format)>::new();

        // Only MKVMergeTextFormat implemented for Read ATM
        result.push((MKVMergeTextFormat::get_extension(), Format::MKVMergeText));

        result
    }

    pub fn get_extension(format: &Format, is_audio_only: bool) -> &'static str {
        match *format {
            Format::CueSheet => CueSheetFormat::get_extension(),
            Format::Flac => "flac",
            Format::Matroska => {
                if !is_audio_only {
                    MatroskaTocFormat::get_extension()
                } else {
                    MatroskaTocFormat::get_audio_extension()
                }
            }
            Format::MKVMergeText => MKVMergeTextFormat::get_extension(),
            Format::MP3 => "mp3",
            Format::Opus => "opus",
            Format::Vorbis => "oga",
            Format::Wave => "wave",
        }
    }

    pub fn get_reader(format: &Format) -> Box<Reader> {
        match *format {
            Format::CueSheet => unimplemented!("Reader for toc::Format::CueSheet"),
            Format::Flac => unimplemented!("Reader for toc::Format::Flac"),
            Format::Matroska => unimplemented!("Reader for toc::Format::Matroska"),
            Format::MKVMergeText => MKVMergeTextFormat::new_as_boxed(),
            Format::MP3 => unimplemented!("Reader for toc::Format::MP3"),
            Format::Opus => unimplemented!("Reader for toc::Format::Opus"),
            Format::Vorbis => unimplemented!("Reader for toc::Format::Vorbis"),
            Format::Wave => unimplemented!("Reader for toc::Format::Wave"),
        }
    }

    pub fn get_writer(format: &Format) -> Box<Writer> {
        match *format {
            Format::CueSheet => CueSheetFormat::new_as_boxed(),
            Format::Flac => unimplemented!("Writer for toc::Format::Wave"),
            Format::Matroska => unimplemented!("Writer for toc::Format::Matroska"),
            Format::MKVMergeText => MKVMergeTextFormat::new_as_boxed(),
            Format::MP3 => unimplemented!("Writer for toc::Format::MP3"),
            Format::Opus => unimplemented!("Writer for toc::Format::Opus"),
            Format::Vorbis => unimplemented!("Writer for toc::Format::Vorbis"),
            Format::Wave => unimplemented!("Writer for toc::Format::Wave"),
        }
    }
}
<|endoftext|>


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

// spell-checker:ignore (paths) wtmp

use std::path::Path;

use clap::{crate_version, App, Arg};
use uucore::utmpx::{self, Utmpx};

static ABOUT: &str = "Print the user names of users currently logged in to the current host";

static ARG_FILES: &str = "files";

fn usage() -> String {
    format!("{0} [FILE]", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    format!(
        "Output who is currently logged in according to FILE.
If FILE is not specified, use {}.  /var/log/wtmp as FILE is common.",
        utmpx::DEFAULT_FILE
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();
    let after_help = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&after_help[..])
        .get_matches_from(args);

    let files: Vec<&Path> = matches
        .values_of_os(ARG_FILES)
        .map(|v| v.map(AsRef::as_ref).collect())
        .unwrap_or_default();

    let filename = if !files.is_empty() {
        files[0]
    } else {
        utmpx::DEFAULT_FILE.as_ref()
    };

    let mut users = Utmpx::iter_all_records_from(filename)
        .filter(Utmpx::is_user_process)
        .map(|ut| ut.user())
        .collect::<Vec<_>>();

    if !users.is_empty() {
        users.sort();
        println!("{}", users.join(" "));
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1))
}
<commit_msg>users: use UResult<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) KokaKiwi <kokakiwi@kokakiwi.net>
//  * (c) Jian Zeng <anonymousknight86@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (paths) wtmp

use std::path::Path;

use clap::{crate_version, App, Arg};
use uucore::error::UResult;
use uucore::utmpx::{self, Utmpx};

static ABOUT: &str = "Print the user names of users currently logged in to the current host";

static ARG_FILES: &str = "files";

fn usage() -> String {
    format!("{0} [FILE]", uucore::execution_phrase())
}

fn get_long_usage() -> String {
    format!(
        "Output who is currently logged in according to FILE.
If FILE is not specified, use {}.  /var/log/wtmp as FILE is common.",
        utmpx::DEFAULT_FILE
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();
    let after_help = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&after_help[..])
        .get_matches_from(args);

    let files: Vec<&Path> = matches
        .values_of_os(ARG_FILES)
        .map(|v| v.map(AsRef::as_ref).collect())
        .unwrap_or_default();

    let filename = if !files.is_empty() {
        files[0]
    } else {
        utmpx::DEFAULT_FILE.as_ref()
    };

    let mut users = Utmpx::iter_all_records_from(filename)
        .filter(Utmpx::is_user_process)
        .map(|ut| ut.user())
        .collect::<Vec<_>>();

    if !users.is_empty() {
        users.sort();
        println!("{}", users.join(" "));
    }

    Ok(())
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1))
}
<|endoftext|>


<commit_before>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs,
    ops::Range,
    panic,
    path::{Path, PathBuf},
    sync::Mutex,
};

use difference::Changeset;
use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        file: file!(),
        line: line!(),
        column: column!(),
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
    pub data: &'static str,
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length =
            file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        let mut updated = "";
        if update_expect() {
            updated = " (updated)";
            rt.per_file
                .entry(expect.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
        }
        let print_help = !rt.help_printed && !update_expect();
        rt.help_printed = true;

        let help = if print_help { HELP } else { "" };

        let diff = Changeset::new(actual, expected, "\n");

        println!(
            "\n
\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m{}
   \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{}
{}
\x1b[1mExpect\x1b[0m:
----
{}
----

\x1b[1mActual\x1b[0m:
----
{}
----

\x1b[1mDiff\x1b[0m:
----
{}
----
",
            updated, expect.file, expect.line, expect.column, help, expected, actual, diff
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos -= delete;
            **pos += insert;
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<commit_msg>Don't fail tests when updating snapshot<commit_after>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs, mem,
    ops::Range,
    panic,
    path::{Path, PathBuf},
    sync::Mutex,
};

use difference::Changeset;
use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        file: file!(),
        line: line!(),
        column: column!(),
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
    pub data: &'static str,
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length =
            file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        if update_expect() {
            println!(
                "\x1b[1m\x1b[92mupdating\x1b[0m: {}:{}:{}",
                expect.file, expect.line, expect.column
            );
            rt.per_file
                .entry(expect.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
            return;
        }
        let print_help = !mem::replace(&mut rt.help_printed, true);
        let help = if print_help { HELP } else { "" };

        let diff = Changeset::new(actual, expected, "\n");

        println!(
            "\n
\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
   \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{}
{}
\x1b[1mExpect\x1b[0m:
----
{}
----

\x1b[1mActual\x1b[0m:
----
{}
----

\x1b[1mDiff\x1b[0m:
----
{}
----
",
            expect.file, expect.line, expect.column, help, expected, actual, diff
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos -= delete;
            **pos += insert;
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<|endoftext|>


<commit_before>pub fn encrypt(text: &str) -> String {
    let text = text.to_lowercase()
        .chars()
        .filter(|c| c.is_lowercase())
        .collect::<Vec<_>>();

    if text.is_empty() {
        return String::new();
    }

    let rows = (text.len() as f64).sqrt() as usize;
    let cols = (text.len() as f64 / rows as f64).ceil() as usize;

    let mut encrypted = String::with_capacity(rows * cols);

    for r in 0..cols {
        for c in 0..cols {
            let index = c * cols + r;

            if index < text.len() {
                encrypted.push(text[index]);
            }
        }

        if r < cols - 1 {
            encrypted.push(' ');
        }
    }

    encrypted
}
<commit_msg>Improve solution for Crypto Square<commit_after>pub fn encrypt(text: &str) -> String {
    let text = text.to_lowercase()
        .chars()
        .filter(|c| c.is_lowercase())
        .collect::<Vec<_>>();

    let size = (text.len() as f64).sqrt().ceil() as usize;

    (0..size)
        .map(|r| {
            (0..size)
                .filter_map(|c| text.get(c * size + r))
                .collect::<String>()
        })
        .collect::<Vec<_>>()
        .join(" ")
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
extern crate libc;

use keycode::{MOD_2, MOD_LOCK};
use layout::Rect;
use std::str;
use std::fmt;
use std::os::env;
use std::c_vec::CVec;
use std::ptr::null_mut;
use std::mem::{uninitialized, transmute};
use std::slice::from_raw_buf;
use self::libc::{c_void, c_int, c_uint, c_char, c_long, c_ulong};
use self::libc::funcs::c95::stdlib::malloc;
use self::XlibEvent::*;
use xinerama::XineramaQueryScreens;
use xlib::*;

extern fn error_handler(display: *mut Display, event: *mut XErrorEvent) -> c_int {
  // TODO: proper error handling
  // HACK: fixes LeaveNotify on invalid windows
  return 0;
}

const KeyPress             : i32 = 2;
const ButtonPress          : i32 = 4;
const EnterNotify          : i32 = 7;
const FocusOut             : i32 = 10;
const Destroy              : i32 = 17;
const UnmapNotify          : i32 = 18;
const MapRequest           : i32 = 20;
const ConfigurationNotify  : i32 = 22;
const ConfigurationRequest : i32 = 23;

pub struct XlibWindowSystem {
  display:   *mut Display,
  root:      Window,
  event:     *mut c_void
}

pub enum XlibEvent {
  XMapRequest(Window),
  XConfigurationNotify(Window),
  XConfigurationRequest(Window, WindowChanges, u32),
  XDestroy(Window),
  XUnmapNotify(Window, bool),
  XEnterNotify(Window),
  XFocusOut(Window),
  XKeyPress(Window, u8, String),
  XButtonPress(Window),
  Ignored
}

pub struct WindowChanges {
  pub x: u32,
  pub y: u32,
  pub width: u32,
  pub height: u32,
  pub border_width: u32,
  pub sibling: Window,
  pub stack_mode: u32,
}

impl fmt::Show for WindowChanges {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{{ x: {}, y: {}, width: {}, height: {}, border_width: {}, sibling: {}, stack_mode: {} }}",
      self.x,
      self.y,
      self.width,
      self.height,
      self.border_width,
      self.sibling,
      self.stack_mode)
  }
}

impl XlibWindowSystem {
  pub fn new() -> XlibWindowSystem {
    unsafe {
      let display = XOpenDisplay(null_mut());
      if display.is_null() {
        error!("Can't open display{}", env().iter().find(|&&(ref x,_)| *x == String::from_str("DISPLAY")).map(|&(_,ref x)| x.clone()).unwrap());
        panic!();
      }

      let root = XDefaultRootWindow(display);
      XSelectInput(display, root, 0x1A0030);
      XDefineCursor(display, root, XCreateFontCursor(display, 68));

      XSetErrorHandler(error_handler as *mut u8);

      XlibWindowSystem {
        display: display,
        root: root,
        event: malloc(256)
      }
    }
  }

  pub fn setup_window(&self, x: u32, y: u32, width: u32, height: u32, border_width: u32, border_color: u32, window: Window) {
    self.set_window_border_width(window, border_width);
    self.set_window_border_color(window, border_color);
    self.move_resize_window(window, x, y, width - (2 * border_width), height - (2 * border_width));
  }

  pub fn configure_window(&self, window: Window, window_changes: WindowChanges, mask: u32) {
    unsafe {
      let mut ret_window_changes = XWindowChanges{
        x: window_changes.x as i32,
        y: window_changes.y as i32,
        width: window_changes.width as i32,
        height: window_changes.height as i32,
        border_width: window_changes.border_width as i32,
        sibling: window_changes.sibling,
        stack_mode: window_changes.stack_mode as i32
      };
      XConfigureWindow(self.display, window, mask, &mut ret_window_changes);
    }
  }

  pub fn unmap_window(&self, window: Window) {
    unsafe {
      XUnmapWindow(self.display, window);
    }
  }

  pub fn map_window(&self, window: Window) {
    unsafe {
      XMapWindow(self.display, window);
    }
  }

  pub fn move_resize_window(&self, window: Window, x: u32, y: u32, width: u32, height: u32) {
    unsafe {
      XMoveResizeWindow(self.display, window, x as i32, y as i32, width, height);
    }
  }

  pub fn focus_window(&self, window: Window, color: u32) {
    unsafe {
      XSetInputFocus(self.display, window, 1, 0);
      self.set_window_border_color(window, color);
      self.sync();
    }
  }

  fn has_protocol(&self, window: Window, protocol: &str) -> bool {
    unsafe {
      let mut count : c_int = uninitialized();
      let mut atoms : *mut Atom = uninitialized();

      XGetWMProtocols(self.display, window, &mut atoms, &mut count);
      CVec::new(atoms, count as uint).as_slice().contains(&XInternAtom(self.display, protocol.to_c_str().as_mut_ptr(), 1))
    }
  }

  pub fn kill_window(&self, window: Window) {
    if window == 0 {
      return;
    }

    unsafe {
      if self.has_protocol(window, "WM_DELETE_WINDOW") {
        let mut msg : XClientMessageEvent = uninitialized();
        msg._type = 33;
        msg.format = 32;
        msg.display = self.display;
        msg.window = window;
        msg.message_type = XInternAtom(self.display, "WM_PROTOCOLS".to_c_str().as_mut_ptr(), 1);
        msg.set_l(&[XInternAtom(self.display, "WM_DELETE_WINDOW".to_c_str().as_mut_ptr(), 1) as u64, 0, 0, 0, 0]);

        XSendEvent(self.display, window, 0, 0, transmute(&msg));
      } else {
        XKillClient(self.display, window);
      }
    }
  }

  pub fn restack_windows(&self, mut windows: Vec<Window>) {
    unsafe {
      XRestackWindows(self.display, windows.as_mut_slice().as_mut_ptr(), windows.len() as i32);
    }
  }

  pub fn sync(&self) {
    unsafe {
      XSync(self.display, 1);
    }
  }

  pub fn grab_button(&self, window: Window) {
    unsafe {
      XGrabButton(self.display, 1, 0x8000, window, 1, 256, 0, 0, 0, 0);
    }
  }

  pub fn grab_modifier(&self, mod_key: u8) {
    unsafe {
      XGrabKey(self.display, 0, mod_key as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_2) as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_LOCK) as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_2 | MOD_LOCK) as u32, self.root, 1, 0, 1);
    }
  }

  pub fn keycode_to_string(&self, keycode: u32) -> String {
    unsafe {
      let keysym = XKeycodeToKeysym(self.display, keycode as u8, 0);
      String::from_str(str::from_c_str(transmute(XKeysymToString(keysym))))
    }
  }

  pub fn set_window_border_width(&self, window: Window, width: u32) {
    if window != self.root {
      unsafe {
        XSetWindowBorderWidth(self.display, window, width);
      }
    }
  }

  pub fn set_window_border_color(&self, window: Window, color: u32) {
    if window != self.root {
      unsafe {
        XSetWindowBorder(self.display, window, color as c_ulong);
      }
    }
  }

  pub fn get_display_width(&self, screen: u32) -> u32 {
    unsafe {
      XDisplayWidth(self.display, screen as i32) as u32
    }
  }

  pub fn get_display_height(&self, screen: u32) -> u32 {
    unsafe {
      XDisplayHeight(self.display, screen as i32) as u32
    }
  }

  pub fn get_display_rect(&self) -> Rect {
    Rect {
      x: 0,
      y: 0,
      width: self.get_display_width(0),
      height: self.get_display_height(0)
    }
  }

  pub fn get_geometry(&self, window: Window) -> Rect {
    unsafe {
      let mut root : Window = uninitialized();
      let mut x : c_int = uninitialized();
      let mut y : c_int = uninitialized();
      let mut width : c_uint = uninitialized();
      let mut height : c_uint = uninitialized();
      let mut depth : c_uint = uninitialized();
      let mut border : c_uint = uninitialized();

      XGetGeometry(self.display, window, &mut root, &mut x, &mut y, &mut width, &mut height, &mut border, &mut depth);

      Rect {
        x: x as u32,
        y: y as u32,
        width: width,
        height: height
      }
    }
  }

  pub fn get_screen_infos(&self) -> Vec<Rect> {
    unsafe {
      let mut num : c_int = 0;
      let screen_ptr = XineramaQueryScreens(self.display, &mut num);

      if num == 0 {
        return vec!(self.get_display_rect());
      }

      from_raw_buf(&screen_ptr, num as uint).iter().map(|ref screen_info|
        Rect {
          x: screen_info.x_org as u32,
          y: screen_info.y_org as u32,
          width: screen_info.width as u32,
          height: screen_info.height as u32
        }
      ).collect()
    }
  }

  pub fn is_window_floating(&self, window: Window) -> bool {
    if self.is_transient_for(window) {
      return true;
    }

    if let Some(hints) = self.get_size_hints(window) {
      return hints.min_width == hints.max_width && hints.min_height == hints.max_height;
    }

    return false;
  }

  fn is_transient_for(&self, window: Window) -> bool {
    unsafe {
      let mut w : Window = uninitialized();

      return XGetTransientForHint(self.display, window, &mut w) == 1;
    }
  }

  pub fn get_size_hints(&self, window: Window) -> Option<XSizeHints> {
    unsafe {
      let mut ret : c_long = uninitialized();
      let mut hints : XSizeHints = uninitialized();

      if XGetWMNormalHints(self.display, window, &mut hints, &mut ret) != 0 {
        Some(hints)
      } else {
        None
      }
    }
  }

  pub fn get_class_name(&self, window: Window) -> String {
    unsafe {
      let mut hint : XClassHint = uninitialized();
      XGetClassHint(self.display, window, &mut hint);
      String::from_str(str::from_c_str(transmute(hint.res_class)))
    }
  }

  pub fn get_window_title(&self, window: Window) -> String {
    if window == self.root {
      return String::from_str("");
    }

    unsafe {
      let mut name : *mut c_char = uninitialized();
      if XFetchName(self.display, window, &mut name) == 3 || name.is_null() {
        String::from_str("")
      } else {
        String::from_str(str::from_c_str(transmute(name)))
      }
    }
  }

  fn cast_event_to<T>(&self) -> &T {
    unsafe {
      &*(self.event as *const T)
    }
  }

  pub fn get_event(&self) -> XlibEvent {
    unsafe {
      XNextEvent(self.display, self.event);
    }

    let evt_type : c_int = *self.cast_event_to();
    match evt_type{
      MapRequest => {
        let evt : &XMapRequestEvent = self.cast_event_to();
        unsafe {
          XSelectInput(self.display, evt.window, 0x420030);
          self.grab_button(evt.window);
        }

        XMapRequest(evt.window)
      },
      ConfigurationNotify => {
        let evt : &XConfigureEvent = self.cast_event_to();
        if evt.window == self.root {
          XConfigurationNotify(evt.window)
        } else {
          Ignored
        }
      },
      ConfigurationRequest => {
        let event : &XConfigureRequestEvent = self.cast_event_to();
        let changes = WindowChanges{
          x: event.x as u32,
          y: event.y as u32,
          width: event.width as u32,
          height: event.height as u32,
          border_width: event.border_width as u32,
          sibling: event.above as Window,
          stack_mode: event.detail as u32
        };
        XConfigurationRequest(event.window, changes, event.value_mask as u32)
      },
      Destroy => {
        let evt : &XDestroyWindowEvent = self.cast_event_to();
        XDestroy(evt.window)
      },
      UnmapNotify => {
        let evt : &XUnmapEvent = self.cast_event_to();
        debug!("UnmapNotify {}", evt.send_event);
        XUnmapNotify(evt.window, evt.send_event > 0)
      },
      EnterNotify => {
        let evt : &XEnterWindowEvent = self.cast_event_to();
        if evt.detail != 2 {
          XEnterNotify(evt.window)
        } else {
          Ignored
        }
      },
      FocusOut => {
        let evt : &XFocusOutEvent = self.cast_event_to();
        if evt.detail != 5 {
          XFocusOut(evt.window)
        } else {
          Ignored
        }
      },
      ButtonPress => {
        let evt : &XButtonPressedEvent = self.cast_event_to();
        unsafe {
          XAllowEvents(self.display, 2, 0);
        }

        XButtonPress(evt.window)
      },
      KeyPress => {
        let evt : &XKeyPressedEvent = self.cast_event_to();
        XKeyPress(evt.window, evt.state as u8, self.keycode_to_string(evt.keycode))
      },
      _ => {
        Ignored
      }
    }
  }
}
<commit_msg>avoid null ptr in get_class_name<commit_after>#![allow(non_upper_case_globals)]
extern crate libc;

use keycode::{MOD_2, MOD_LOCK};
use layout::Rect;
use std::str;
use std::fmt;
use std::os::env;
use std::c_vec::CVec;
use std::ptr::null_mut;
use std::mem::{uninitialized, transmute};
use std::slice::from_raw_buf;
use self::libc::{c_void, c_int, c_uint, c_char, c_long, c_ulong};
use self::libc::funcs::c95::stdlib::malloc;
use self::XlibEvent::*;
use xinerama::XineramaQueryScreens;
use xlib::*;

extern fn error_handler(display: *mut Display, event: *mut XErrorEvent) -> c_int {
  // TODO: proper error handling
  // HACK: fixes LeaveNotify on invalid windows
  return 0;
}

const KeyPress             : i32 = 2;
const ButtonPress          : i32 = 4;
const EnterNotify          : i32 = 7;
const FocusOut             : i32 = 10;
const Destroy              : i32 = 17;
const UnmapNotify          : i32 = 18;
const MapRequest           : i32 = 20;
const ConfigurationNotify  : i32 = 22;
const ConfigurationRequest : i32 = 23;

pub struct XlibWindowSystem {
  display:   *mut Display,
  root:      Window,
  event:     *mut c_void
}

pub enum XlibEvent {
  XMapRequest(Window),
  XConfigurationNotify(Window),
  XConfigurationRequest(Window, WindowChanges, u32),
  XDestroy(Window),
  XUnmapNotify(Window, bool),
  XEnterNotify(Window),
  XFocusOut(Window),
  XKeyPress(Window, u8, String),
  XButtonPress(Window),
  Ignored
}

pub struct WindowChanges {
  pub x: u32,
  pub y: u32,
  pub width: u32,
  pub height: u32,
  pub border_width: u32,
  pub sibling: Window,
  pub stack_mode: u32,
}

impl fmt::Show for WindowChanges {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{{ x: {}, y: {}, width: {}, height: {}, border_width: {}, sibling: {}, stack_mode: {} }}",
      self.x,
      self.y,
      self.width,
      self.height,
      self.border_width,
      self.sibling,
      self.stack_mode)
  }
}

impl XlibWindowSystem {
  pub fn new() -> XlibWindowSystem {
    unsafe {
      let display = XOpenDisplay(null_mut());
      if display.is_null() {
        error!("Can't open display{}", env().iter().find(|&&(ref x,_)| *x == String::from_str("DISPLAY")).map(|&(_,ref x)| x.clone()).unwrap());
        panic!();
      }

      let root = XDefaultRootWindow(display);
      XSelectInput(display, root, 0x1A0030);
      XDefineCursor(display, root, XCreateFontCursor(display, 68));

      XSetErrorHandler(error_handler as *mut u8);

      XlibWindowSystem {
        display: display,
        root: root,
        event: malloc(256)
      }
    }
  }

  pub fn setup_window(&self, x: u32, y: u32, width: u32, height: u32, border_width: u32, border_color: u32, window: Window) {
    self.set_window_border_width(window, border_width);
    self.set_window_border_color(window, border_color);
    self.move_resize_window(window, x, y, width - (2 * border_width), height - (2 * border_width));
  }

  pub fn configure_window(&self, window: Window, window_changes: WindowChanges, mask: u32) {
    unsafe {
      let mut ret_window_changes = XWindowChanges{
        x: window_changes.x as i32,
        y: window_changes.y as i32,
        width: window_changes.width as i32,
        height: window_changes.height as i32,
        border_width: window_changes.border_width as i32,
        sibling: window_changes.sibling,
        stack_mode: window_changes.stack_mode as i32
      };
      XConfigureWindow(self.display, window, mask, &mut ret_window_changes);
    }
  }

  pub fn unmap_window(&self, window: Window) {
    unsafe {
      XUnmapWindow(self.display, window);
    }
  }

  pub fn map_window(&self, window: Window) {
    unsafe {
      XMapWindow(self.display, window);
    }
  }

  pub fn move_resize_window(&self, window: Window, x: u32, y: u32, width: u32, height: u32) {
    unsafe {
      XMoveResizeWindow(self.display, window, x as i32, y as i32, width, height);
    }
  }

  pub fn focus_window(&self, window: Window, color: u32) {
    unsafe {
      XSetInputFocus(self.display, window, 1, 0);
      self.set_window_border_color(window, color);
      self.sync();
    }
  }

  fn has_protocol(&self, window: Window, protocol: &str) -> bool {
    unsafe {
      let mut count : c_int = uninitialized();
      let mut atoms : *mut Atom = uninitialized();

      XGetWMProtocols(self.display, window, &mut atoms, &mut count);
      CVec::new(atoms, count as uint).as_slice().contains(&XInternAtom(self.display, protocol.to_c_str().as_mut_ptr(), 1))
    }
  }

  pub fn kill_window(&self, window: Window) {
    if window == 0 {
      return;
    }

    unsafe {
      if self.has_protocol(window, "WM_DELETE_WINDOW") {
        let mut msg : XClientMessageEvent = uninitialized();
        msg._type = 33;
        msg.format = 32;
        msg.display = self.display;
        msg.window = window;
        msg.message_type = XInternAtom(self.display, "WM_PROTOCOLS".to_c_str().as_mut_ptr(), 1);
        msg.set_l(&[XInternAtom(self.display, "WM_DELETE_WINDOW".to_c_str().as_mut_ptr(), 1) as u64, 0, 0, 0, 0]);

        XSendEvent(self.display, window, 0, 0, transmute(&msg));
      } else {
        XKillClient(self.display, window);
      }
    }
  }

  pub fn restack_windows(&self, mut windows: Vec<Window>) {
    unsafe {
      XRestackWindows(self.display, windows.as_mut_slice().as_mut_ptr(), windows.len() as i32);
    }
  }

  pub fn sync(&self) {
    unsafe {
      XSync(self.display, 1);
    }
  }

  pub fn grab_button(&self, window: Window) {
    unsafe {
      XGrabButton(self.display, 1, 0x8000, window, 1, 256, 0, 0, 0, 0);
    }
  }

  pub fn grab_modifier(&self, mod_key: u8) {
    unsafe {
      XGrabKey(self.display, 0, mod_key as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_2) as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_LOCK) as u32, self.root, 1, 0, 1);
      XGrabKey(self.display, 0, (mod_key | MOD_2 | MOD_LOCK) as u32, self.root, 1, 0, 1);
    }
  }

  pub fn keycode_to_string(&self, keycode: u32) -> String {
    unsafe {
      let keysym = XKeycodeToKeysym(self.display, keycode as u8, 0);
      String::from_str(str::from_c_str(transmute(XKeysymToString(keysym))))
    }
  }

  pub fn set_window_border_width(&self, window: Window, width: u32) {
    if window != self.root {
      unsafe {
        XSetWindowBorderWidth(self.display, window, width);
      }
    }
  }

  pub fn set_window_border_color(&self, window: Window, color: u32) {
    if window != self.root {
      unsafe {
        XSetWindowBorder(self.display, window, color as c_ulong);
      }
    }
  }

  pub fn get_display_width(&self, screen: u32) -> u32 {
    unsafe {
      XDisplayWidth(self.display, screen as i32) as u32
    }
  }

  pub fn get_display_height(&self, screen: u32) -> u32 {
    unsafe {
      XDisplayHeight(self.display, screen as i32) as u32
    }
  }

  pub fn get_display_rect(&self) -> Rect {
    Rect {
      x: 0,
      y: 0,
      width: self.get_display_width(0),
      height: self.get_display_height(0)
    }
  }

  pub fn get_geometry(&self, window: Window) -> Rect {
    unsafe {
      let mut root : Window = uninitialized();
      let mut x : c_int = uninitialized();
      let mut y : c_int = uninitialized();
      let mut width : c_uint = uninitialized();
      let mut height : c_uint = uninitialized();
      let mut depth : c_uint = uninitialized();
      let mut border : c_uint = uninitialized();

      XGetGeometry(self.display, window, &mut root, &mut x, &mut y, &mut width, &mut height, &mut border, &mut depth);

      Rect {
        x: x as u32,
        y: y as u32,
        width: width,
        height: height
      }
    }
  }

  pub fn get_screen_infos(&self) -> Vec<Rect> {
    unsafe {
      let mut num : c_int = 0;
      let screen_ptr = XineramaQueryScreens(self.display, &mut num);

      if num == 0 {
        return vec!(self.get_display_rect());
      }

      from_raw_buf(&screen_ptr, num as uint).iter().map(|ref screen_info|
        Rect {
          x: screen_info.x_org as u32,
          y: screen_info.y_org as u32,
          width: screen_info.width as u32,
          height: screen_info.height as u32
        }
      ).collect()
    }
  }

  pub fn is_window_floating(&self, window: Window) -> bool {
    if self.is_transient_for(window) {
      return true;
    }

    if let Some(hints) = self.get_size_hints(window) {
      return hints.min_width == hints.max_width && hints.min_height == hints.max_height;
    }

    return false;
  }

  fn is_transient_for(&self, window: Window) -> bool {
    unsafe {
      let mut w : Window = uninitialized();

      return XGetTransientForHint(self.display, window, &mut w) == 1;
    }
  }

  pub fn get_size_hints(&self, window: Window) -> Option<XSizeHints> {
    unsafe {
      let mut ret : c_long = uninitialized();
      let mut hints : XSizeHints = uninitialized();

      if XGetWMNormalHints(self.display, window, &mut hints, &mut ret) != 0 {
        Some(hints)
      } else {
        None
      }
    }
  }

  pub fn get_class_name(&self, window: Window) -> String {
    unsafe {
      let mut hint : XClassHint = uninitialized();
      if XGetClassHint(self.display, window, &mut hint) != 0 || hint.res_class.is_null() {
        String::from_str("")
      } else {
        String::from_str(str::from_c_str(transmute(hint.res_class)))
      }
    }
  }

  pub fn get_window_title(&self, window: Window) -> String {
    if window == self.root {
      return String::from_str("");
    }

    unsafe {
      let mut name : *mut c_char = uninitialized();
      if XFetchName(self.display, window, &mut name) == 3 || name.is_null() {
        String::from_str("")
      } else {
        String::from_str(str::from_c_str(transmute(name)))
      }
    }
  }

  fn cast_event_to<T>(&self) -> &T {
    unsafe {
      &*(self.event as *const T)
    }
  }

  pub fn get_event(&self) -> XlibEvent {
    unsafe {
      XNextEvent(self.display, self.event);
    }

    let evt_type : c_int = *self.cast_event_to();
    match evt_type{
      MapRequest => {
        let evt : &XMapRequestEvent = self.cast_event_to();
        unsafe {
          XSelectInput(self.display, evt.window, 0x420030);
          self.grab_button(evt.window);
        }

        XMapRequest(evt.window)
      },
      ConfigurationNotify => {
        let evt : &XConfigureEvent = self.cast_event_to();
        if evt.window == self.root {
          XConfigurationNotify(evt.window)
        } else {
          Ignored
        }
      },
      ConfigurationRequest => {
        let event : &XConfigureRequestEvent = self.cast_event_to();
        let changes = WindowChanges{
          x: event.x as u32,
          y: event.y as u32,
          width: event.width as u32,
          height: event.height as u32,
          border_width: event.border_width as u32,
          sibling: event.above as Window,
          stack_mode: event.detail as u32
        };
        XConfigurationRequest(event.window, changes, event.value_mask as u32)
      },
      Destroy => {
        let evt : &XDestroyWindowEvent = self.cast_event_to();
        XDestroy(evt.window)
      },
      UnmapNotify => {
        let evt : &XUnmapEvent = self.cast_event_to();
        debug!("UnmapNotify {}", evt.send_event);
        XUnmapNotify(evt.window, evt.send_event > 0)
      },
      EnterNotify => {
        let evt : &XEnterWindowEvent = self.cast_event_to();
        if evt.detail != 2 {
          XEnterNotify(evt.window)
        } else {
          Ignored
        }
      },
      FocusOut => {
        let evt : &XFocusOutEvent = self.cast_event_to();
        if evt.detail != 5 {
          XFocusOut(evt.window)
        } else {
          Ignored
        }
      },
      ButtonPress => {
        let evt : &XButtonPressedEvent = self.cast_event_to();
        unsafe {
          XAllowEvents(self.display, 2, 0);
        }

        XButtonPress(evt.window)
      },
      KeyPress => {
        let evt : &XKeyPressedEvent = self.cast_event_to();
        XKeyPress(evt.window, evt.state as u8, self.keycode_to_string(evt.keycode))
      },
      _ => {
        Ignored
      }
    }
  }
}
<|endoftext|>


<commit_before>use core::num::Zero;
use nalgebra::traits::translation::Translation;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::workarounds::scalar_op::{ScalarMul, ScalarDiv};
use geom::transformed::Transformed;
use geom::ball::Ball;
use contact::Contact;

fn update_collide_ball_ball
   <V: Add<V, V> + Sub<V, V> + Norm<T> + ScalarMul<T> + ScalarDiv<T> + Copy,
    T: Real + Copy>
   (c1: &V, b1: &Ball<T>, c2: &V, b2: &Ball<T>, out: &mut Contact<V, T>) -> bool
{
  let r1         = b1.radius;
  let r2         = b2.radius;
  let delta_pos  = *c2 - *c1;
  let sqdist     = delta_pos.sqnorm();
  let sum_radius = r1 + r2;

  if (sqdist < sum_radius * sum_radius)
  {
    let normal = delta_pos.normalized();
    out.set(&(c1 - normal.scalar_mul(&r1)),
            &(c2 - normal.scalar_mul(&r2)),
            &normal,
            &(sum_radius - sqdist.sqrt()));
    true
  }
  else
  { false }
}

fn collide_ball_ball
   <V: Add<V, V> + Sub<V, V> + Norm<T> + ScalarMul<T> + ScalarDiv<T> + Copy + Zero,
    T: Real + Copy>
   (c1: &V, b1: &Ball<T>, c2: &V, b2: &Ball<T>) -> Option<~Contact<V, T>>
{
  let mut res =
    ~Contact { world_contact1: Zero::zero(),
               world_contact2: Zero::zero(),
               center:         Zero::zero(),
               normal:         Zero::zero(),
               depth:          Zero::zero() };

  if (update_collide_ball_ball(c1, b1, c2, b2, res))
  { Some(res) }
  else
  { None }
}

fn update_collide_tball_tball
   <T: Translation<V>,
    V: Add<V, V> + Sub<V, V> + Norm<N> + ScalarMul<N> + ScalarDiv<N> + Copy,
    N: Real + Copy>
   (b1:  &Transformed<Ball<N>, T>,
    b2:  &Transformed<Ball<N>, T>,
    out: &mut Contact<V, N>) -> bool
{
  update_collide_ball_ball(&b1.t.translation(), b1.g,
                           &b2.t.translation(), b2.g,
                           out)
}

fn collide_tball_tball
   <T: Translation<V>,
    V: Add<V, V> + Sub<V, V> + Norm<N> + ScalarMul<N> + ScalarDiv<N> + Copy + Zero,
    N: Real + Copy>
   (b1:  &Transformed<Ball<N>, T>,
    b2:  &Transformed<Ball<N>, T>) -> Option<~Contact<V, N>>
{
  collide_ball_ball(&b1.t.translation(), b1.g, &b2.t.translation(), b2.g)
}
<commit_msg>ball vs ball now uses a simplified detector.<commit_after>use core::num::Zero;
use nalgebra::traits::translation::Translation;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::vector_space::VectorSpace;
use geom::transformed::Transformed;
use geom::ball::Ball;
use contact::Contact;

fn update_collide_ball_ball<V: VectorSpace<T> + Norm<T> + Copy, T: Real + Copy>
   (c1: &V, b1: &Ball<T>, c2: &V, b2: &Ball<T>, out: &mut Contact<V, T>) -> bool
{
  let r1         = b1.radius;
  let r2         = b2.radius;
  let delta_pos  = *c2 - *c1;
  let sqdist     = delta_pos.sqnorm();
  let sum_radius = r1 + r2;

  if (sqdist < sum_radius * sum_radius)
  {
    let normal = delta_pos.normalized();
    out.set(&(c1 - normal.scalar_mul(&r1)),
            &(c2 - normal.scalar_mul(&r2)),
            &normal,
            &(sum_radius - sqdist.sqrt()));
    true
  }
  else
  { false }
}

fn collide_ball_ball<V: VectorSpace<T> + Norm<T> + Copy, T: Real + Copy>
   (c1: &V, b1: &Ball<T>, c2: &V, b2: &Ball<T>) -> Option<~Contact<V, T>>
{
  let mut res =
    ~Contact { world_contact1: Zero::zero(),
               world_contact2: Zero::zero(),
               center:         Zero::zero(),
               normal:         Zero::zero(),
               depth:          Zero::zero() };

  if (update_collide_ball_ball(c1, b1, c2, b2, res))
  { Some(res) }
  else
  { None }
}

fn update_collide_tball_tball
   <T: Translation<V>,
    V: VectorSpace<N> + Norm<N> + Copy,
    N: Real + Copy>
   (b1:  &Transformed<Ball<N>, T>,
    b2:  &Transformed<Ball<N>, T>,
    out: &mut Contact<V, N>) -> bool
{
  update_collide_ball_ball(&b1.t.translation(), b1.g,
                           &b2.t.translation(), b2.g,
                           out)
}

fn collide_tball_tball
   <T: Translation<V>,
    V: VectorSpace<N> + Norm<N> + Copy,
    N: Real + Copy>
   (b1:  &Transformed<Ball<N>, T>,
    b2:  &Transformed<Ball<N>, T>) -> Option<~Contact<V, N>>
{
  collide_ball_ball(&b1.t.translation(), b1.g, &b2.t.translation(), b2.g)
}
<|endoftext|>


<commit_before>#![crate_type = "dylib"]
#![feature(plugin_registrar, rustc_private, quote)]

extern crate rustc;
extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::ast::{FnDecl, Ident, ItemKind, LitKind, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
use syntax::codemap::Span;
use syntax::symbol::Symbol;

fn get_js_name(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem) -> String {
    let meta_items = meta_item.meta_item_list().unwrap_or_else(|| {
        ecx.struct_span_err(span, "incorrect use of napi_callback attribute")
            .help("usage: #[napi_callback(\"jsFunctionName\")]")
            .emit();
        ecx.span_fatal(span, "invalid attribute usage");
    });

    if meta_items.len() != 1 {
        ecx.struct_span_err(span, "incorrect use of napi_callback attribute")
            .help("usage: #[napi_callback(\"jsFunctionName\")]")
            .emit();
        ecx.span_fatal(span, "attribute requires exactly one argument");
    }

    let name_meta_item = &meta_items[0];

    let name = name_meta_item
        .literal()
        .and_then(|literal| match literal.node {
            LitKind::Str(ref s, _) => Some(s.as_str()),
            _ => None,
        })
        .unwrap_or_else(|| {
            ecx.span_fatal(
                name_meta_item.span(),
                "attribute argument must be a string",
            );
        });

    String::from(&*name)
}

struct Function {
    pub ident: Ident,
    pub decl: FnDecl,
}

impl Function {
    fn from_annotatable(
        ecx: &mut ExtCtxt,
        annotated: &Annotatable,
    ) -> Function {
        let report_error = |span| {
            ecx.span_fatal(
                span,
                "napi_callback attribute can only be used on \
                 functions, but was applied to this item.",
            );
        };

        match *annotated {
            Annotatable::Item(ref item) => match item.node {
                ItemKind::Fn(ref decl, ..) => Function {
                    ident: item.ident,
                    decl: decl.clone().unwrap(),
                },
                _ => report_error(item.span),
            },
            Annotatable::TraitItem(ref item) => report_error(item.span),
            Annotatable::ImplItem(ref item) => report_error(item.span),
        }
    }
}

fn create_napi_callback(ecx: &mut ExtCtxt, function: &Function) -> Annotatable {
    let fn_ident = function.ident;
    let extern_fn_name = format!("napi_rs_cb_{}", fn_ident.name);
    let extern_fn_ident = Ident::from_str(extern_fn_name.as_str());

    Annotatable::Item(
        quote_item!(ecx,
            #[no_mangle]
            pub extern "C" fn $extern_fn_ident(
                env: ::napi::sys::napi_env,
                _info: ::napi::sys::napi_callback_info,
            ) -> ::napi::sys::napi_value {
                use std::error::Error;
                use std::ffi::CString;
                use std::ptr;

                use ::napi::{NapiEnv, NapiResult, NapiValue};
                use ::napi::sys::{napi_get_undefined, napi_throw,
                                  napi_throw_error, napi_value};

                let env_wrapper = NapiEnv::from(env);

                fn typecheck_result<T: NapiValue>(_: &NapiResult<T>) {}

                let result = $fn_ident(&env_wrapper);
                typecheck_result(&result);

                match result {
                    Ok(value) => value.as_sys_value(),
                    Err(error) => {
                        if let Some(exception) = error.exception {
                            unsafe {
                                napi_throw(env, exception);
                            }
                        } else {
                            let message = format!("{}", &error);
                            let c_string =
                                CString::new(message).unwrap_or_else(|_| {
                                    CString::new(error.description()).unwrap()
                                });

                            unsafe {
                                napi_throw_error(
                                    env,
                                    ptr::null(),
                                    c_string.as_ptr(),
                                );
                            }
                        }

                        let mut result: napi_value = ptr::null_mut();
                        unsafe {
                            napi_get_undefined(env, &mut result);
                        }
                        result
                    }
                }
            }
        ).unwrap(),
    )
}

pub fn napi_callback_decorator(
    ecx: &mut ExtCtxt,
    span: Span,
    meta_item: &MetaItem,
    annotated: Annotatable,
) -> Vec<Annotatable> {
    let js_name = get_js_name(ecx, span, meta_item);
    let function = Function::from_annotatable(ecx, &annotated);

    let mut output = Vec::new();

    output.push(create_napi_callback(ecx, &function));
    output.push(annotated);

    output
}

#[plugin_registrar]
pub fn plugin_registrar(registry: &mut Registry) {
    registry.register_syntax_extension(
        Symbol::intern("napi_callback"),
        SyntaxExtension::MultiModifier(Box::new(napi_callback_decorator)),
    );
}
<commit_msg>style: prepend _ to an unused variable name<commit_after>#![crate_type = "dylib"]
#![feature(plugin_registrar, rustc_private, quote)]

extern crate rustc;
extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::ast::{FnDecl, Ident, ItemKind, LitKind, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
use syntax::codemap::Span;
use syntax::symbol::Symbol;

fn get_js_name(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem) -> String {
    let meta_items = meta_item.meta_item_list().unwrap_or_else(|| {
        ecx.struct_span_err(span, "incorrect use of napi_callback attribute")
            .help("usage: #[napi_callback(\"jsFunctionName\")]")
            .emit();
        ecx.span_fatal(span, "invalid attribute usage");
    });

    if meta_items.len() != 1 {
        ecx.struct_span_err(span, "incorrect use of napi_callback attribute")
            .help("usage: #[napi_callback(\"jsFunctionName\")]")
            .emit();
        ecx.span_fatal(span, "attribute requires exactly one argument");
    }

    let name_meta_item = &meta_items[0];

    let name = name_meta_item
        .literal()
        .and_then(|literal| match literal.node {
            LitKind::Str(ref s, _) => Some(s.as_str()),
            _ => None,
        })
        .unwrap_or_else(|| {
            ecx.span_fatal(
                name_meta_item.span(),
                "attribute argument must be a string",
            );
        });

    String::from(&*name)
}

struct Function {
    pub ident: Ident,
    pub decl: FnDecl,
}

impl Function {
    fn from_annotatable(
        ecx: &mut ExtCtxt,
        annotated: &Annotatable,
    ) -> Function {
        let report_error = |span| {
            ecx.span_fatal(
                span,
                "napi_callback attribute can only be used on \
                 functions, but was applied to this item.",
            );
        };

        match *annotated {
            Annotatable::Item(ref item) => match item.node {
                ItemKind::Fn(ref decl, ..) => Function {
                    ident: item.ident,
                    decl: decl.clone().unwrap(),
                },
                _ => report_error(item.span),
            },
            Annotatable::TraitItem(ref item) => report_error(item.span),
            Annotatable::ImplItem(ref item) => report_error(item.span),
        }
    }
}

fn create_napi_callback(ecx: &mut ExtCtxt, function: &Function) -> Annotatable {
    let fn_ident = function.ident;
    let extern_fn_name = format!("napi_rs_cb_{}", fn_ident.name);
    let extern_fn_ident = Ident::from_str(extern_fn_name.as_str());

    Annotatable::Item(
        quote_item!(ecx,
            #[no_mangle]
            pub extern "C" fn $extern_fn_ident(
                env: ::napi::sys::napi_env,
                _info: ::napi::sys::napi_callback_info,
            ) -> ::napi::sys::napi_value {
                use std::error::Error;
                use std::ffi::CString;
                use std::ptr;

                use ::napi::{NapiEnv, NapiResult, NapiValue};
                use ::napi::sys::{napi_get_undefined, napi_throw,
                                  napi_throw_error, napi_value};

                let env_wrapper = NapiEnv::from(env);

                fn typecheck_result<T: NapiValue>(_: &NapiResult<T>) {}

                let result = $fn_ident(&env_wrapper);
                typecheck_result(&result);

                match result {
                    Ok(value) => value.as_sys_value(),
                    Err(error) => {
                        if let Some(exception) = error.exception {
                            unsafe {
                                napi_throw(env, exception);
                            }
                        } else {
                            let message = format!("{}", &error);
                            let c_string =
                                CString::new(message).unwrap_or_else(|_| {
                                    CString::new(error.description()).unwrap()
                                });

                            unsafe {
                                napi_throw_error(
                                    env,
                                    ptr::null(),
                                    c_string.as_ptr(),
                                );
                            }
                        }

                        let mut result: napi_value = ptr::null_mut();
                        unsafe {
                            napi_get_undefined(env, &mut result);
                        }
                        result
                    }
                }
            }
        ).unwrap(),
    )
}

pub fn napi_callback_decorator(
    ecx: &mut ExtCtxt,
    span: Span,
    meta_item: &MetaItem,
    annotated: Annotatable,
) -> Vec<Annotatable> {
    let _js_name = get_js_name(ecx, span, meta_item);
    let function = Function::from_annotatable(ecx, &annotated);

    let mut output = Vec::new();

    output.push(create_napi_callback(ecx, &function));
    output.push(annotated);

    output
}

#[plugin_registrar]
pub fn plugin_registrar(registry: &mut Registry) {
    registry.register_syntax_extension(
        Symbol::intern("napi_callback"),
        SyntaxExtension::MultiModifier(Box::new(napi_callback_decorator)),
    );
}
<|endoftext|>