<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate cc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::{run, native_lib_boilerplate};

fn main() {
    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    if target.contains("bitrig") || target.contains("cloudabi") || target.contains("emscripten") ||
       target.contains("fuchsia") || target.contains("msvc") || target.contains("openbsd") ||
       target.contains("redox") || target.contains("rumprun") || target.contains("wasm32") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let link_name = if target.contains("windows") { "jemalloc" } else { "jemalloc_pic" };
    let native = match native_lib_boilerplate("jemalloc", "jemalloc", link_name, "lib") {
        Ok(native) => native,
        _ => return,
    };

    let mut cmd = Command::new("sh");
    cmd.arg(native.src_dir.join("configure")
                          .to_str()
                          .unwrap()
                          .replace("C:\\", "/c/")
                          .replace("\\", "/"))
       .current_dir(&native.out_dir)
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default());

    if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") || target.contains("musl") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    // FIXME: building with jemalloc assertions is currently broken.
    // See <https://github.com/rust-lang/rust/issues/44152>.
    //if cfg!(feature = "debug") {
    //    cmd.arg("--enable-debug");
    //}

    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    // for some reason, jemalloc configure doesn't detect this value
    // automatically for this target
    if target == "sparc64-unknown-linux-gnu" {
        cmd.arg("--with-lg-quantum=4");
    }

    run(&mut cmd);

    let mut make = Command::new(build_helper::make(&host));
    make.current_dir(&native.out_dir)
        .arg("build_lib_static");

    // These are intended for mingw32-make which we don't use
    if cfg!(windows) {
        make.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    }

    // mingw make seems... buggy? unclear...
    if !host.contains("windows") {
        make.arg("-j")
            .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"));
    }

    run(&mut make);

    // The pthread_atfork symbols is used by jemalloc on android but the really
    // old android we're building on doesn't have them defined, so just make
    // sure the symbols are available.
    if target.contains("androideabi") {
        println!("cargo:rerun-if-changed=pthread_atfork_dummy.c");
        cc::Build::new()
            .flag("-fvisibility=hidden")
            .file("pthread_atfork_dummy.c")
            .compile("pthread_atfork_dummy");
    }
}
<commit_msg>Rollup merge of #47554 - EdSchouten:cloudabi-jemalloc, r=nikomatsakis<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate cc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::{run, native_lib_boilerplate};

fn main() {
    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    if target.contains("bitrig") || target.contains("emscripten") || target.contains("fuchsia") ||
       target.contains("msvc") || target.contains("openbsd") || target.contains("redox") ||
       target.contains("rumprun") || target.contains("wasm32") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    // CloudABI ships with a copy of jemalloc that has been patched to
    // work well with sandboxing. Don't attempt to build our own copy,
    // as it won't build.
    if target.contains("cloudabi") {
        return;
    }

    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let link_name = if target.contains("windows") { "jemalloc" } else { "jemalloc_pic" };
    let native = match native_lib_boilerplate("jemalloc", "jemalloc", link_name, "lib") {
        Ok(native) => native,
        _ => return,
    };

    let mut cmd = Command::new("sh");
    cmd.arg(native.src_dir.join("configure")
                          .to_str()
                          .unwrap()
                          .replace("C:\\", "/c/")
                          .replace("\\", "/"))
       .current_dir(&native.out_dir)
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default());

    if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") || target.contains("musl") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    // FIXME: building with jemalloc assertions is currently broken.
    // See <https://github.com/rust-lang/rust/issues/44152>.
    //if cfg!(feature = "debug") {
    //    cmd.arg("--enable-debug");
    //}

    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    // for some reason, jemalloc configure doesn't detect this value
    // automatically for this target
    if target == "sparc64-unknown-linux-gnu" {
        cmd.arg("--with-lg-quantum=4");
    }

    run(&mut cmd);

    let mut make = Command::new(build_helper::make(&host));
    make.current_dir(&native.out_dir)
        .arg("build_lib_static");

    // These are intended for mingw32-make which we don't use
    if cfg!(windows) {
        make.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    }

    // mingw make seems... buggy? unclear...
    if !host.contains("windows") {
        make.arg("-j")
            .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"));
    }

    run(&mut make);

    // The pthread_atfork symbols is used by jemalloc on android but the really
    // old android we're building on doesn't have them defined, so just make
    // sure the symbols are available.
    if target.contains("androideabi") {
        println!("cargo:rerun-if-changed=pthread_atfork_dummy.c");
        cc::Build::new()
            .flag("-fvisibility=hidden")
            .file("pthread_atfork_dummy.c")
            .compile("pthread_atfork_dummy");
    }
}
<|endoftext|>


<commit_before>use crate::comment::CommentStyle;
use std::fmt::{self, Display};

#[derive(new)]
pub(super) struct DocCommentFormatter<'a> {
    literal: &'a str,
    style: CommentStyle<'a>,
}

impl Display for DocCommentFormatter<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let opener = self.style.opener().trim_end();
        let mut lines = self.literal.lines().peekable();
        while let Some(line) = lines.next() {
            let is_last_line = lines.peek().is_none();
            if is_last_line {
                write!(formatter, "{}{}", opener, line)?;
            } else {
                writeln!(formatter, "{}{}", opener, line)?;
            }
        }
        Ok(())
    }
}

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

    #[test]
    fn literal_controls_leading_spaces() {
        test_doc_comment_is_formatted_correctly(
            "    Lorem ipsum",
            "///    Lorem ipsum",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn single_line_doc_comment_is_formatted_correctly() {
        test_doc_comment_is_formatted_correctly(
            "Lorem ipsum",
            "///Lorem ipsum",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn multi_line_doc_comment_is_formatted_correctly() {
        test_doc_comment_is_formatted_correctly(
            "Lorem ipsum\nDolor sit amet",
            "///Lorem ipsum\n///Dolor sit amet",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn whitespace_within_lines_is_preserved() {
        test_doc_comment_is_formatted_correctly(
            " Lorem ipsum \n Dolor sit amet ",
            "/// Lorem ipsum \n/// Dolor sit amet ",
            CommentStyle::TripleSlash,
        );
    }

    fn test_doc_comment_is_formatted_correctly(
        literal: &str,
        expected_comment: &str,
        style: CommentStyle<'_>,
    ) {
        assert_eq!(
            expected_comment,
            format!("{}", DocCommentFormatter::new(&literal, style))
        );
    }
}
<commit_msg>Document DocCommentFormatter<commit_after>use crate::comment::CommentStyle;
use std::fmt::{self, Display};

/// Formats a string as a doc comment using the given [`CommentStyle`].
#[derive(new)]
pub(super) struct DocCommentFormatter<'a> {
    literal: &'a str,
    style: CommentStyle<'a>,
}

impl Display for DocCommentFormatter<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let opener = self.style.opener().trim_end();
        let mut lines = self.literal.lines().peekable();
        while let Some(line) = lines.next() {
            let is_last_line = lines.peek().is_none();
            if is_last_line {
                write!(formatter, "{}{}", opener, line)?;
            } else {
                writeln!(formatter, "{}{}", opener, line)?;
            }
        }
        Ok(())
    }
}

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

    #[test]
    fn literal_controls_leading_spaces() {
        test_doc_comment_is_formatted_correctly(
            "    Lorem ipsum",
            "///    Lorem ipsum",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn single_line_doc_comment_is_formatted_correctly() {
        test_doc_comment_is_formatted_correctly(
            "Lorem ipsum",
            "///Lorem ipsum",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn multi_line_doc_comment_is_formatted_correctly() {
        test_doc_comment_is_formatted_correctly(
            "Lorem ipsum\nDolor sit amet",
            "///Lorem ipsum\n///Dolor sit amet",
            CommentStyle::TripleSlash,
        );
    }

    #[test]
    fn whitespace_within_lines_is_preserved() {
        test_doc_comment_is_formatted_correctly(
            " Lorem ipsum \n Dolor sit amet ",
            "/// Lorem ipsum \n/// Dolor sit amet ",
            CommentStyle::TripleSlash,
        );
    }

    fn test_doc_comment_is_formatted_correctly(
        literal: &str,
        expected_comment: &str,
        style: CommentStyle<'_>,
    ) {
        assert_eq!(
            expected_comment,
            format!("{}", DocCommentFormatter::new(&literal, style))
        );
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs::File;
use std::io::{ BufWriter, Read, Write };
use std::path::Path;

use reqwest;
use reqwest::header::ContentLength;

pub fn download_file_while<F>(url: &str, output: &Path, and: F) -> Result<u64, String>
    where F: Fn(u64, &u64) {

    // Abandoning this code, as the filename is going to be unpredictable in this case.
    /*let mut output_to = PathBuf::new();
            output_to.push(output);
            output_to.push(url.split('/').last().unwrap_or("_error_"));*/

    let response = get!(reqwest::get(url), "Error occured while making a GET request. Reason:\n");
    let target = get!(File::create(output), "Error occured while creating file. Reason:\n");
    let mut target = BufWriter::new(target);

    let length = match response.headers().get::<ContentLength>() {
        Some(x) => **x,
        None => 0u64
    };

    let mut bytes_completed = 0u64;
    for byte in response.bytes() {
        target.write(&[byte.unwrap()]).unwrap();
        bytes_completed += 1;
        and(length, &bytes_completed)
    }

    Ok(length)
}

pub fn download_file_and<F>(url: &str, output: &Path, and: F) -> Result<u64, String>
    where F: Fn(u64) {
    let length = try!(download_file_while(url, output, |_, _| { }));
    and(length);
    Ok(length)
}

pub fn download_file(url: &str, output: &Path) -> Result<u64, String> {
    download_file_and(url, output, |_| { }) // Do nothing for and.
}
<commit_msg>Documentation for download.rs<commit_after>use std::error::Error;
use std::fs::File;
use std::io::{ BufWriter, Read, Write };
use std::path::Path;

use reqwest;
use reqwest::header::ContentLength;

/// Downloads file from specified URL, and output it
/// to the path specified in `output`. While downloading
/// the file, for each byte downloaded, it invokes `and`.
/// When the download is complete, it returns a Result that
/// returns file size if download succeeds, or returns error
/// message if download fails.
pub fn download_file_while<F>(url: &str, output: &Path, and: F) -> Result<u64, String>
    where F: Fn(u64, &u64) {

    // Abandoning this code, as the filename is going to be unpredictable in this case.
    //let mut output_to = PathBuf::new();
    //        output_to.push(output);
    //        output_to.push(url.split('/').last().unwrap_or("_error_"));*/

    let response = get!(reqwest::get(url), "Error occured while making a GET request. Reason:\n");
    let target = get!(File::create(output), "Error occured while creating file. Reason:\n");
    let mut target = BufWriter::new(target);

    let length = match response.headers().get::<ContentLength>() {
        Some(x) => **x,
        None => 0u64
    };

    let mut bytes_completed = 0u64;
    for byte in response.bytes() {
        target.write(&[byte.unwrap()]).unwrap();
        bytes_completed += 1;
        and(length, &bytes_completed)
    }

    Ok(length)
}

/// Downloads file from specified URL, and output it
/// to the path specified in `output`. After the file is
/// downloaded, it invokes `and`.
/// When the download is complete, it returns a Result that
/// returns file size if download succeeds, or returns error
/// message if download fails.
pub fn download_file_and<F>(url: &str, output: &Path, and: F) -> Result<u64, String>
    where F: Fn(u64) {
    let length = try!(download_file_while(url, output, |_, _| { }));
    and(length);
    Ok(length)
}

/// Downloads file from specified URL, and output it to
/// the path specified in `output`.
/// When the download is complete, it returns a Result that
/// returns file size if download succeeds, or returns error
/// message if download fails.
pub fn download_file(url: &str, output: &Path) -> Result<u64, String> {
    download_file_and(url, output, |_| { }) // Do nothing for and.
}
<|endoftext|>


<commit_before>extern crate dcpu16;
extern crate getopts;

use std::env;
use std::fs::File;
use std::io::Read;
use std::error::Error;
use std::path::Path;
use getopts::Options;
use dcpu16::dcpu;
use dcpu16::disassembler;

fn main() {
    let mut opts = Options::new();
    let args: Vec<String> = env::args().collect();
    opts.optflag("m", "no-color", "no color");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(why) => {
            println!("{}", why);
            return;
        },
    };

    if matches.free.len() != 1 {
        println!("Please input file");
        return;
    }
    let color = !matches.opt_present("m");
    let ref filename = matches.free[0];

    let mut cpu = dcpu::DCPU::new();

    let path = Path::new(filename);
    let mut file = match File::open(&path) {
        Err(why) => {
            println!("Could not open file {}: {}", path.display(), why.description());
            return;
        },
        Ok(f) => f,
    };

    let mut nwords = 0usize;
    let mut buffer: Vec<u8> = Vec::new();
    let res = file.read_to_end(&mut buffer);
    match res {
        Ok(_) => {
            let mut j = 0;
            let mut sig = 0u16;
            for v in buffer {
                if j % 2 == 0 {
                    sig = v as u16;
                } else {
                    cpu.mem[nwords] = (sig << 8) + (v as u16);
                    nwords += 1;
                }

                j += 1;
            }
        }
        Err(why) => {
            println!("Could not read contents of file: {}", why);
            return;
        },
    }

    loop {
        if cpu.pc as usize > nwords {
            break;
        }
        let (offset, s) = disassembler::disassemble_instruction(&cpu, color);
        cpu.pc += offset;
        println!("{}", s);
    }
}
<commit_msg>Printed one too many in disassembler.<commit_after>extern crate dcpu16;
extern crate getopts;

use std::env;
use std::fs::File;
use std::io::Read;
use std::error::Error;
use std::path::Path;
use getopts::Options;
use dcpu16::dcpu;
use dcpu16::disassembler;

fn main() {
    let mut opts = Options::new();
    let args: Vec<String> = env::args().collect();
    opts.optflag("m", "no-color", "no color");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(why) => {
            println!("{}", why);
            return;
        },
    };

    if matches.free.len() != 1 {
        println!("Please input file");
        return;
    }
    let color = !matches.opt_present("m");
    let ref filename = matches.free[0];

    let mut cpu = dcpu::DCPU::new();

    let path = Path::new(filename);
    let mut file = match File::open(&path) {
        Err(why) => {
            println!("Could not open file {}: {}", path.display(), why.description());
            return;
        },
        Ok(f) => f,
    };

    let mut nwords = 0usize;
    let mut buffer: Vec<u8> = Vec::new();
    let res = file.read_to_end(&mut buffer);
    match res {
        Ok(_) => {
            let mut j = 0;
            let mut sig = 0u16;
            for v in buffer {
                if j % 2 == 0 {
                    sig = v as u16;
                } else {
                    cpu.mem[nwords] = (sig << 8) + (v as u16);
                    nwords += 1;
                }

                j += 1;
            }
        }
        Err(why) => {
            println!("Could not read contents of file: {}", why);
            return;
        },
    }

    loop {
        if cpu.pc as usize >= nwords {
            break;
        }
        let (offset, s) = disassembler::disassemble_instruction(&cpu, color);
        cpu.pc += offset;
        println!("{}", s);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate rustc_serialize;

use std::env;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::parse_config;
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};
use rustc_serialize::json;

use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::child_config::ChildConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    try!(try!(scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = try!(parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()));
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            if cfg.exists() {
                let all_children: BTreeMap<String, ChildConfig>;
                all_children = try!(parse_config(&cfg, &childval, Default::default())
                    .map_err(|e| format!("Can't read child config {:?}: {}",
                                         sandbox_config.config_file, e)));
                for child in all_children.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            } else {
                info!("No current processes for {}", sandbox_name);
            }

            let logname = master.config_log_dir
                .join(format!("{}.log", sandbox_name));
            if logname.exists() {
                // TODO(tailhook) look in log rotations
                let log = try!(File::open(&logname)
                    .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
                let mut configs;
                configs = VecDeque::<(Tm, BTreeMap<String, ChildConfig>)>::new();
                for (line_no, line) in BufReader::new(log).lines().enumerate() {
                    let line = try!(line
                        .map_err(|e| format!("Readline error: {}", e)));
                    let mut iter = line.splitn(2, " ");
                    let (tm, cfg) = match (iter.next(), iter.next()) {
                            (Some(""), None) => continue, // last line, probably
                            (Some(date), Some(config)) => {
                                // TODO(tailhook) remove or check sandbox name
                                (try!(time::strptime(date, "%Y-%m-%dT%H:%M:%SZ")
                                     .map_err(|_| format!("Bad time at {:?}:{}",
                                        logname, line_no))),
                                 try!(json::decode(config)
                                     .map_err(|_| format!("Bad config at {:?}:{}",
                                        logname, line_no))),
                                )
                            }
                            _ => {
                                return Err(format!("Bad line at {:?}:{}",
                                                   logname, line_no));
                            }
                        };
                    if let Some(&(_, ref ocfg)) = configs.back(){
                        if *ocfg == cfg {
                            continue;
                        }
                    }
                    configs.push_back((tm, cfg));
                    if configs.len() >= ver_max as usize {
                        configs.pop_front();
                    }
                }
                min_time.map(|min_time| {
                    while configs.len() > ver_min as usize {
                        if let Some(&(tm, _)) = configs.front() {
                            if tm > min_time {
                                break;
                            }
                        } else {
                            break;
                        }
                        configs.pop_front();
                    }
                });
                for &(_, ref cfg) in configs.iter() {
                    for child in cfg.values() {
                        // Current are always added
                        images.insert(sandbox_config.image_dir.join(&child.image));
                    }
                }
            } else {
                info!("No log for {} probably never used", sandbox_name);
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))));
    Ok((images, image_dirs))
}
<commit_msg>lithos_clean: smart ignore of broken lines in log<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate rustc_serialize;

use std::env;
use std::rc::Rc;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::parse_config;
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};
use rustc_serialize::json;

use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::child_config::ChildConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

enum Candidate {
    Config(Tm, BTreeMap<String, ChildConfig>),
    BrokenLine(Rc<PathBuf>, usize),
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

impl PartialEq for Candidate {
    fn eq(&self, other: &Candidate) -> bool {
        use Candidate::*;
        match (self, other) {
            (&BrokenLine(..), &BrokenLine(..)) => true,
            (&Config(_, ref a), &Config(_, ref b)) => a == b,
            _ => false,
        }
    }
}

fn parse_line(line: &str) -> Result<(Tm, BTreeMap<String, ChildConfig>), ()> {
    let mut iter = line.splitn(2, " ");
    let date = iter.next().ok_or(())?;
    let config = iter.next().ok_or(())?;
    Ok((
        time::strptime(date, "%Y-%m-%dT%H:%M:%SZ").map_err(|_| ())?,
        json::decode(config).map_err(|_| ())?,
    ))
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    try!(try!(scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = try!(parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()));
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            if cfg.exists() {
                let all_children: BTreeMap<String, ChildConfig>;
                all_children = try!(parse_config(&cfg, &childval, Default::default())
                    .map_err(|e| format!("Can't read child config {:?}: {}",
                                         sandbox_config.config_file, e)));
                for child in all_children.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            } else {
                info!("No current processes for {}", sandbox_name);
            }

            let logname = Rc::new(master.config_log_dir
                .join(format!("{}.log", sandbox_name)));
            if logname.exists() {
                // TODO(tailhook) look in log rotations
                let log = try!(File::open(&*logname)
                    .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
                let mut configs;
                configs = VecDeque::<Candidate>::new();
                for (line_no, line) in BufReader::new(log).lines().enumerate() {
                    let line = try!(line
                        .map_err(|e| format!("Readline error: {}", e)));
                    if line.trim() == "" {
                        continue; // Probably last line
                    }
                    let cand = match parse_line(&line) {
                        Ok((tm, cfg)) => Candidate::Config(tm, cfg),
                        Err(()) => {
                            warn!("Broken line {}: {}",
                                logname.display(), line_no);
                            Candidate::BrokenLine(logname.clone(), line_no)
                        }
                    };
                    if configs.back() == Some(&cand) {
                        continue;
                    }
                    configs.push_back(cand);
                    if configs.len() >= ver_max as usize {
                        configs.pop_front();
                    }
                }
                min_time.map(|min_time| {
                    while configs.len() > ver_min as usize {
                        match configs.front() {
                            Some(&Candidate::Config(tm, _)) if tm > min_time
                            => {
                                break;
                            }
                            _ => {}
                        }
                        configs.pop_front();
                    }
                });
                for cand in configs.iter() {
                    match *cand {
                        Candidate::Config(_, ref cfg) => {
                            for child in cfg.values() {
                                // Current are always added
                                images.insert(
                                    sandbox_config.image_dir
                                    .join(&child.image));
                            }
                        }
                        Candidate::BrokenLine(..) => {
                            return Err(format!("Can't reliably find out \
                                used images for sandbox {}", sandbox_name));
                        }
                    }
                }
            } else {
                info!("No log for {} probably never used", sandbox_name);
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))));
    Ok((images, image_dirs))
}
<|endoftext|>


<commit_before>
extern crate elf;

use std::path::Path;

fn main() {
    let path = Path::new("stress");
    let file = match elf::File::open_path(&path) {
        Ok(f) => f,
        Err(e) => panic!("Error: {:?}", e),
    };
    println!("Debug-print ELF file:");
    println!("{:?}", file);
    println!("");
    println!("Pretty-print ELF file:");
    println!("{}", file);

    println!("Getting the .text section");
    let text = file.get_section(".text");
    match text {
        Some(s) => println!("shdr: {}", s),
        None => println!("Failed to look up .text section!"),
    }
}
<commit_msg>Accept multiple arguments from `rust-readelf`.<commit_after>
extern crate elf;

use std::env;
use std::path::PathBuf;

fn main() {
    let args: Vec<String> = env::args().collect();
    let paths: Vec<PathBuf> = if args.len() == 1 {
        vec!(From::from("stress"))
    } else {
        let mut i = args.into_iter();
        i.next();
        i.map(|arg| From::from(arg) )
            .collect()
    };
    for path in paths.into_iter() {
        let file = match elf::File::open_path(&path) {
            Ok(f) => f,
            Err(e) => panic!("Error: {:?}", e),
        };
        println!("Debug-print ELF file:");
        println!("{:?}", file);
        println!("");
        println!("Pretty-print ELF file:");
        println!("{}", file);

        println!("Getting the .text section");
        let text = file.get_section(".text");
        match text {
            Some(s) => println!("shdr: {}", s),
            None => println!("Failed to look up .text section!"),
        }
    }
}
<|endoftext|>


<commit_before>extern crate winapi;
extern crate kernel32;
extern crate user32;
extern crate shell32;
extern crate libc;

use winapi::windef::{HWND, HMENU, HICON, HBRUSH};
use winapi::winnt::{LPCWSTR};
use winapi::minwindef::{UINT, DWORD, WPARAM, LPARAM, LRESULT, HINSTANCE};
use winapi::winuser::{WNDCLASSW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT};
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;

fn to_wstring(str : &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
}

pub unsafe extern "system" fn window_proc(h_wnd :HWND,
	                                        msg :UINT,
                                          w_param :WPARAM,
                                          l_param :LPARAM) -> LRESULT
{
    if msg == winapi::winuser::WM_DESTROY {
        user32::PostQuitMessage(0);
    }
    return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
}

fn main() {

    // Create window

    let class_name = to_wstring("my_window");
    let instance;
    unsafe {
        instance = kernel32::GetModuleHandleA(std::ptr::null_mut());
    };
    let wnd;
    unsafe {
        wnd = WNDCLASSW {
            style: 0,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: 0 as HINSTANCE,
            hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
            hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
            hbrBackground: 16 as HBRUSH,
            lpszMenuName: 0 as LPCWSTR,
            lpszClassName: class_name.as_ptr(),
        };
    }
    let hwnd;
    unsafe {
        user32::RegisterClassW(&wnd);
        hwnd = user32::CreateWindowExW(0,
                                       class_name.as_ptr(),
                                       to_wstring("rust_systray_window").as_ptr(),
                                       WS_OVERLAPPEDWINDOW,
                                       CW_USEDEFAULT,
                                       0,
                                       CW_USEDEFAULT,
                                       0,
                                       0 as HWND,
                                       0 as HMENU,
                                       0 as HINSTANCE,
                                       std::ptr::null_mut());
        println!("Got window! {:?}", hwnd as u32);
        println!("Error? {}", kernel32::GetLastError());
    }

    // Create and add icon

    let icon;
    unsafe {
        icon = user32::LoadImageA(instance,
                                  std::mem::transmute(0x1 as isize),
                                  winapi::IMAGE_ICON,
                                  64,
                                  64,
                                  0) as HICON;
    }
    println!("Got icon! {:?}", icon as u32);
    let a : [i8; 64] = [0; 64];
    let mut b : [i8; 128] = [0; 128];
    let g : [i8; 256] = [0; 256];
    let guid = winapi::GUID {
        Data1: 0 as winapi::c_ulong,
        Data2: 0 as winapi::c_ushort,
        Data3: 0 as winapi::c_ushort,
        Data4: [0; 8]
    };
    let mut nid_add = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD, //This should actually be the struct size :|
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_MESSAGE,
        uCallbackMessage: winapi::WM_USER + 1,
        hIcon: 0 as HICON,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Adding icon! {}", shell32::Shell_NotifyIconA(winapi::NIM_ADD,
                                                               &mut nid_add as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    let mut nid = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD, //This should actually be the struct size :|
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_ICON,
        uCallbackMessage: winapi::WM_USER + 1,
        hIcon: icon,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Setting icon! {}", shell32::Shell_NotifyIconA(winapi::NIM_MODIFY,
                                                                &mut nid as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    // Add Tooltip
    // Gross way to convert String to [i8; 128]
    // TODO: Clean up conversion, test for length so we don't panic at runtime
    let t = "Test Tip".to_string();
    let tt = t.as_bytes();
    for i in 0..tt.len() {
        b[i] = tt[i].clone() as i8;
    }
    let mut nid_tip = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD, //This should actually be the struct size :|
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_TIP,
        uCallbackMessage: winapi::WM_USER + 1,
        hIcon: 0 as HICON,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Setting tip! {}", shell32::Shell_NotifyIconA(winapi::NIM_MODIFY,
                                                                &mut nid_tip as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    // Add Menu

    // Run message loop

    let mut msg = winapi::winuser::MSG {
        hwnd: 0 as HWND,
        message: 0 as UINT,
        wParam: 0 as WPARAM,
        lParam: 0 as LPARAM,
        time: 0 as DWORD,
        pt: winapi::windef::POINT { x: 0, y: 0, },
    };
    let mut pm;
    loop {
        unsafe {
            pm = user32::GetMessageW(&mut msg, 0 as HWND, 0, 0);
        }

        if pm == 0 {
            continue;
        }

        if msg.message == winapi::winuser::WM_QUIT {
            break;
        }
        unsafe {
            user32::TranslateMessage(&mut msg);
            user32::DispatchMessageW(&mut msg);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>feat: Menu setup<commit_after>extern crate winapi;
extern crate kernel32;
extern crate user32;
extern crate shell32;
extern crate libc;

use winapi::windef::{HWND, HMENU, HICON, HBRUSH};
use winapi::winnt::{LPCWSTR};
use winapi::minwindef::{UINT, DWORD, WPARAM, LPARAM, LRESULT, HINSTANCE};
use winapi::winuser::{WNDCLASSW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT};
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;

fn to_wstring(str : &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
}

pub unsafe extern "system" fn window_proc(h_wnd :HWND,
	                                        msg :UINT,
                                          w_param :WPARAM,
                                          l_param :LPARAM) -> LRESULT
{
    if msg == winapi::winuser::WM_USER + 1 {
        println!("Got callback message! {}", l_param);
        if l_param as UINT == winapi::winuser::WM_LBUTTONUP {
            println!("Posting quit message! {}", l_param);
            user32::PostQuitMessage(0);
        }
    }
    if msg == winapi::winuser::WM_DESTROY {
        user32::PostQuitMessage(0);
    }
    return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
}

fn main() {

    // Create window

    let class_name = to_wstring("my_window");
    let instance;
    unsafe {
        instance = kernel32::GetModuleHandleA(std::ptr::null_mut());
    };
    let wnd;
    unsafe {
        wnd = WNDCLASSW {
            style: 0,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: 0 as HINSTANCE,
            hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
            hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
            hbrBackground: 16 as HBRUSH,
            lpszMenuName: 0 as LPCWSTR,
            lpszClassName: class_name.as_ptr(),
        };
    }
    let hwnd;
    unsafe {
        user32::RegisterClassW(&wnd);
        hwnd = user32::CreateWindowExW(0,
                                       class_name.as_ptr(),
                                       to_wstring("rust_systray_window").as_ptr(),
                                       WS_OVERLAPPEDWINDOW,
                                       CW_USEDEFAULT,
                                       0,
                                       CW_USEDEFAULT,
                                       0,
                                       0 as HWND,
                                       0 as HMENU,
                                       0 as HINSTANCE,
                                       std::ptr::null_mut());
        println!("Got window! {:?}", hwnd as u32);
        println!("Error? {}", kernel32::GetLastError());
    }

    // Create and add icon

    let icon;
    unsafe {
        icon = user32::LoadImageA(instance,
                                  std::mem::transmute(0x1 as isize),
                                  winapi::IMAGE_ICON,
                                  64,
                                  64,
                                  0) as HICON;
    }
    println!("Got icon! {:?}", icon as u32);
    let a : [i8; 64] = [0; 64];
    let mut b : [i8; 128] = [0; 128];
    let g : [i8; 256] = [0; 256];
    let guid = winapi::GUID {
        Data1: 0 as winapi::c_ulong,
        Data2: 0 as winapi::c_ushort,
        Data3: 0 as winapi::c_ushort,
        Data4: [0; 8]
    };
    let mut nid_add = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD,
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_MESSAGE,
        uCallbackMessage: winapi::WM_USER + 1,
        hIcon: 0 as HICON,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Adding icon! {}", shell32::Shell_NotifyIconA(winapi::NIM_ADD,
                                                               &mut nid_add as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    let mut nid = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD,
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_ICON,
        uCallbackMessage: 0 as UINT,
        hIcon: icon,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Setting icon! {}", shell32::Shell_NotifyIconA(winapi::NIM_MODIFY,
                                                                &mut nid as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    // Add Tooltip
    // Gross way to convert String to [i8; 128]
    // TODO: Clean up conversion, test for length so we don't panic at runtime
    let t = "Test Tip".to_string();
    let tt = t.as_bytes();
    for i in 0..tt.len() {
        b[i] = tt[i].clone() as i8;
    }
    let mut nid_tip = winapi::shellapi::NOTIFYICONDATAA {
        cbSize: std::mem::size_of::<winapi::shellapi::NOTIFYICONDATAA>() as DWORD,
        hWnd: hwnd,
        uID: 0x1 as UINT,
        uFlags: winapi::NIF_TIP,
        uCallbackMessage: 0 as UINT,
        hIcon: 0 as HICON,
        szTip: b,
        dwState: 0 as DWORD,
        dwStateMask: 0 as DWORD,
        szInfo: g,
        uTimeout: 0 as UINT,
        szInfoTitle: a,
        dwInfoFlags: 0 as UINT,
        guidItem: guid,
        hBalloonIcon: 0 as HICON
    };
    unsafe {
        println!("Setting tip! {}", shell32::Shell_NotifyIconA(winapi::NIM_MODIFY,
                                                                &mut nid_tip as *mut winapi::shellapi::NOTIFYICONDATAA));
    }

    // Add Menu
    unsafe {
        let menu = user32::CreatePopupMenu();
        let m = winapi::MENUINFO {
            cbSize: std::mem::size_of::<winapi::MENUINFO>() as DWORD,
            fMask: winapi::MIM_APPLYTOSUBMENUS | winapi::MIM_STYLE,
            dwStyle: winapi::MNS_NOTIFYBYPOS,
            cyMax: 0 as UINT,
            hbrBack: 0 as HBRUSH,
            dwContextHelpID: 0 as DWORD,
            dwMenuData: 0 as winapi::ULONG_PTR
        };
        println!("Created menu! {}", user32::SetMenuInfo(menu, &m as *const winapi::MENUINFO));
    }
    // Run message loop

    let mut msg = winapi::winuser::MSG {
        hwnd: 0 as HWND,
        message: 0 as UINT,
        wParam: 0 as WPARAM,
        lParam: 0 as LPARAM,
        time: 0 as DWORD,
        pt: winapi::windef::POINT { x: 0, y: 0, },
    };
    loop {
        unsafe {
            user32::GetMessageW(&mut msg, 0 as HWND, 0, 0);
        }
        if msg.message == winapi::winuser::WM_QUIT {
            break;
        }
        unsafe {
            user32::TranslateMessage(&mut msg);
            user32::DispatchMessageW(&mut msg);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?.trim_start_matches(&path.to_string_lossy().as_ref());
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<commit_msg>Fix panic when the start matches<commit_after>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| {
                    println!("'{}' : {}", completion, e_index);
                    escape(&[tilde, &completion[e_index..]].concat())
                })
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<|endoftext|>


<commit_before>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::t;
use ignore::WalkBuilder;
use std::path::Path;
use std::process::Command;

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    assert!(status.success(), "running {} successful", cmd_debug);
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<commit_msg>Rollup merge of #68188 - JohnTitor:tweak-assertion-note, r=Mark-Simulacrum<commit_after>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::t;
use ignore::WalkBuilder;
use std::path::Path;
use std::process::Command;

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    if !status.success() {
        eprintln!(
            "Running `{}` failed.\nIf you're running `tidy`, \
            try again with `--bless` flag. Or, you just want to format \
            code, run `./x.py fmt` instead.",
            cmd_debug,
        );
        std::process::exit(1);
    }
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<|endoftext|>