<commit_before>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate ignore;
extern crate num_cpus;

pub mod lscolors;
pub mod fshelper;

use std::borrow::Cow;
use std::env;
use std::error::Error;
use std::io::Write;
use std::ops::Deref;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, Component};
use std::process;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::thread;
use std::time;

use clap::{App, AppSettings, Arg};
use atty::Stream;
use regex::{Regex, RegexBuilder};
use ignore::WalkBuilder;

use lscolors::LsColors;

/// Defines how to display search result paths.
#[derive(PartialEq)]
enum PathDisplay {
    /// As an absolute path
    Absolute,

    /// As a relative path
    Relative
}

/// Configuration options for *fd*.
struct FdOptions {
    /// Determines whether the regex search is case-sensitive or case-insensitive.
    case_sensitive: bool,

    /// Whether to search within the full file path or just the base name (filename or directory
    /// name).
    search_full_path: bool,

    /// Whether to ignore hidden files and directories (or not).
    ignore_hidden: bool,

    /// Whether to respect VCS ignore files (`.gitignore`, `.ignore`, ..) or not.
    read_ignore: bool,

    /// Whether to follow symlinks or not.
    follow_links: bool,

    /// Whether elements of output should be separated by a null character
    null_separator: bool,

    /// The maximum search depth, or `None` if no maximum search depth should be set.
    ///
    /// A depth of `1` includes all files under the current directory, a depth of `2` also includes
    /// all files under subdirectories of the current directory, etc.
    max_depth: Option<usize>,

    /// The number of threads to use.
    threads: usize,

    /// Time to buffer results internally before streaming to the console. This is useful to
    /// provide a sorted output, in case the total execution time is shorter than
    /// `max_buffer_time`.
    max_buffer_time: Option<time::Duration>,

    /// Display results as relative or absolute path.
    path_display: PathDisplay,

    /// `None` if the output should not be colorized. Otherwise, a `LsColors` instance that defines
    /// how to style different filetypes.
    ls_colors: Option<LsColors>
}

/// The receiver thread can either be buffering results or directly streaming to the console.
enum ReceiverMode {
    /// Receiver is still buffering in order to sort the results, if the search finishes fast
    /// enough.
    Buffering,

    /// Receiver is directly printing results to the output.
    Streaming
}

/// Root directory
static ROOT_DIR : &'static str = "/";

/// Parent directory
static PARENT_DIR : &'static str = "..";

/// Print a search result to the console.
fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
    let path_full = base.join(entry);

    let path_str = entry.to_string_lossy();

    #[cfg(target_family = "unix")]
    let is_executable = |p: &std::path::PathBuf| {
        p.metadata()
         .ok()
         .map(|f| f.permissions().mode() & 0o111 != 0)
         .unwrap_or(false)
    };

    #[cfg(not(target_family = "unix"))]
    let is_executable = |_: &std::path::PathBuf| { false };

    let stdout = std::io::stdout();
    let mut handle = stdout.lock();

    if let Some(ref ls_colors) = config.ls_colors {
        let default_style = ansi_term::Style::default();

        let mut component_path = base.to_path_buf();

        if config.path_display == PathDisplay::Absolute {
            print!("{}", ls_colors.directory.paint(ROOT_DIR));
        }

        // Traverse the path and colorize each component
        for component in entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p.to_string_lossy(),
                Component::ParentDir => Cow::from(PARENT_DIR),
                _                    => error("Error: unexpected path component.")
            };

            component_path.push(Path::new(comp_str.deref()));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    &ls_colors.symlink
                } else if component_path.is_dir() {
                    &ls_colors.directory
                } else if is_executable(&component_path) {
                    &ls_colors.executable
                } else {
                    // Look up file name
                    let o_style =
                        component_path.file_name()
                                      .and_then(|n| n.to_str())
                                      .and_then(|n| ls_colors.filenames.get(n));

                    match o_style {
                        Some(s) => s,
                        None =>
                            // Look up file extension
                            component_path.extension()
                                          .and_then(|e| e.to_str())
                                          .and_then(|e| ls_colors.extensions.get(e))
                                          .unwrap_or(&default_style)
                    }
                };

            write!(handle, "{}", style.paint(comp_str)).ok();

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                write!(handle, "{}", style.paint(sep)).ok();
            }
        }

        let r = if config.null_separator {
          write!(handle, "{}", '\0')
        } else {
          writeln!(handle, "")
        };
        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    } else {
        // Uncolorized output

        let prefix = if config.path_display == PathDisplay::Absolute { ROOT_DIR } else { "" };
        let separator = if config.null_separator { "\0" } else { "\n" };

        let r = write!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator);

        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    }
}

/// Recursively scan the given search path and search for files / pathnames matching the pattern.
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
    let (tx, rx) = channel();

    let walker = WalkBuilder::new(root)
                     .hidden(config.ignore_hidden)
                     .ignore(config.read_ignore)
                     .git_ignore(config.read_ignore)
                     .parents(config.read_ignore)
                     .git_global(config.read_ignore)
                     .git_exclude(config.read_ignore)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .threads(config.threads)
                     .build_parallel();

    // Spawn the thread that receives all results through the channel.
    let rx_config = Arc::clone(&config);
    let rx_base = base.to_owned();
    let receiver_thread = thread::spawn(move || {
        let start = time::Instant::now();

        let mut buffer = vec!();

        // Start in buffering mode
        let mut mode = ReceiverMode::Buffering;

        // Maximum time to wait before we start streaming to the console.
        let max_buffer_time = rx_config.max_buffer_time
                                       .unwrap_or_else(|| time::Duration::from_millis(100));

        for value in rx {
            match mode {
                ReceiverMode::Buffering => {
                    buffer.push(value);

                    // Have we reached the maximum time?
                    if time::Instant::now() - start > max_buffer_time {
                        // Flush the buffer
                        for v in &buffer {
                            print_entry(&rx_base, v, &rx_config);
                        }
                        buffer.clear();

                        // Start streaming
                        mode = ReceiverMode::Streaming;
                    }
                },
                ReceiverMode::Streaming => {
                    print_entry(&rx_base, &value, &rx_config);
                }
            }
        }

        // If we have finished fast enough (faster than max_buffer_time), we haven't streamed
        // anything to the console, yet. In this case, sort the results and print them:
        if !buffer.is_empty() {
            buffer.sort();
            for value in buffer {
                print_entry(&rx_base, &value, &rx_config);
            }
        }
    });

    // Spawn the sender threads.
    walker.run(|| {
        let base = base.to_owned();
        let config = config.clone();
        let pattern = pattern.clone();
        let tx_thread = tx.clone();

        Box::new(move |entry_o| {
            let entry = match entry_o {
                Ok(e) => e,
                Err(_) => return ignore::WalkState::Continue
            };

            let path_rel_buf = match fshelper::path_relative_from(entry.path(), &*base) {
                Some(p) => p,
                None => error("Error: could not get relative path for directory entry.")
            };
            let path_rel = path_rel_buf.as_path();

            let search_str_o =
                if config.search_full_path {
                    Some(path_rel.to_string_lossy())
                } else {
                    path_rel.file_name()
                            .map(|f| f.to_string_lossy())
                };

            if let Some(search_str) = search_str_o {
                // TODO: take care of the unwrap call
                pattern.find(&*search_str)
                       .map(|_| tx_thread.send(path_rel_buf.to_owned()).unwrap());
            }

            ignore::WalkState::Continue
        })
    });

    // Drop the initial sender. If we don't do this, the receiver will block even
    // if all threads have finished, since there is still one sender around.
    drop(tx);

    // Wait for the receiver thread to print out all results.
    receiver_thread.join().unwrap();
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let matches =
        App::new("fd")
            .version(crate_version!())
            .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
            .setting(AppSettings::ColoredHelp)
            .setting(AppSettings::DeriveDisplayOrder)
            .arg(Arg::with_name("case-sensitive")
                        .long("case-sensitive")
                        .short("s")
                        .help("Case-sensitive search (default: smart case)"))
            .arg(Arg::with_name("full-path")
                        .long("full-path")
                        .short("p")
                        .help("Search full path (default: file-/dirname only)"))
            .arg(Arg::with_name("hidden")
                        .long("hidden")
                        .short("H")
                        .help("Search hidden files and directories"))
            .arg(Arg::with_name("no-ignore")
                        .long("no-ignore")
                        .short("I")
                        .help("Do not respect .(git)ignore files"))
            .arg(Arg::with_name("follow")
                        .long("follow")
                        .short("L")
                        .help("Follow symlinks")
                        .alias("dereference"))
            .arg(Arg::with_name("null_separator")
                        .long("print0")
                        .short("0")
                        .help("Separate results by the null character"))
            .arg(Arg::with_name("absolute-path")
                        .long("absolute-path")
                        .short("a")
                        .help("Show absolute instead of relative paths"))
            .arg(Arg::with_name("no-color")
                        .long("no-color")
                        .short("n")
                        .help("Do not colorize output"))
            .arg(Arg::with_name("depth")
                        .long("max-depth")
                        .short("d")
                        .takes_value(true)
                        .help("Set maximum search depth (default: none)"))
            .arg(Arg::with_name("threads")
                        .long("threads")
                        .short("j")
                        .takes_value(true)
                        .help("The number of threads used for searching"))
            .arg(Arg::with_name("max-buffer-time")
                        .long("max-buffer-time")
                        .takes_value(true)
                        .help("the time (in ms) to buffer, before streaming to the console")
                        .hidden(true))
            .arg(Arg::with_name("pattern")
                        .help("the search pattern, a regular expression (optional)"))
            .arg(Arg::with_name("path")
                        .help("the root directory for the filesystem search (optional)"))
            .get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Error: could not get current directory.")
    };
    let current_dir = current_dir_buf.as_path();

    // Get the root directory for the search
    let mut root_dir_is_absolute = false;
    let root_dir_buf = if let Some(rd) = matches.value_of("path") {
        let path = Path::new(rd);

        root_dir_is_absolute = path.is_absolute();

        path.canonicalize().unwrap_or_else(
            |_| error(&format!("Error: could not find directory '{}'.", rd))
        )
    } else {
        current_dir_buf.clone()
    };

    if !root_dir_buf.is_dir() {
        error(&format!("Error: '{}' is not a directory.", root_dir_buf.to_string_lossy()));
    }

    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = matches.is_present("case-sensitive") ||
                         pattern.chars().any(char::is_uppercase);

    let colored_output = !matches.is_present("no-color") &&
                         atty::is(Stream::Stdout);

    let ls_colors =
        if colored_output {
            Some(
                env::var("LS_COLORS")
                    .ok()
                    .map(|val| LsColors::from_string(&val))
                    .unwrap_or_default()
            )
        } else {
            None
        };

    let config = FdOptions {
        case_sensitive:    case_sensitive,
        search_full_path:  matches.is_present("full-path"),
        ignore_hidden:     !matches.is_present("hidden"),
        read_ignore:       !matches.is_present("no-ignore"),
        follow_links:      matches.is_present("follow"),
        null_separator:    matches.is_present("null_separator"),
        max_depth:         matches.value_of("depth")
                                   .and_then(|n| usize::from_str_radix(n, 10).ok()),
        threads:           std::cmp::max(
                             matches.value_of("threads")
                                    .and_then(|n| usize::from_str_radix(n, 10).ok())
                                    .unwrap_or(num_cpus::get()),
                             1
                           ),
        max_buffer_time:   matches.value_of("max-buffer-time")
                                  .and_then(|n| u64::from_str_radix(n, 10).ok())
                                  .map(time::Duration::from_millis),
        path_display:      if matches.is_present("absolute-path") || root_dir_is_absolute {
                               PathDisplay::Absolute
                           } else {
                               PathDisplay::Relative
                           },
        ls_colors:         ls_colors
    };

    let root = Path::new(ROOT_DIR);
    let base = match config.path_display {
        PathDisplay::Relative => current_dir,
        PathDisplay::Absolute => root
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(root_dir, Arc::new(re), base, Arc::new(config)),
        Err(err) => error(err.description())
    }
}
<commit_msg>Fix clippy warnings<commit_after>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate ignore;
extern crate num_cpus;

pub mod lscolors;
pub mod fshelper;

use std::borrow::Cow;
use std::env;
use std::error::Error;
use std::io::Write;
use std::ops::Deref;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, Component};
use std::process;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::thread;
use std::time;

use clap::{App, AppSettings, Arg};
use atty::Stream;
use regex::{Regex, RegexBuilder};
use ignore::WalkBuilder;

use lscolors::LsColors;

/// Defines how to display search result paths.
#[derive(PartialEq)]
enum PathDisplay {
    /// As an absolute path
    Absolute,

    /// As a relative path
    Relative
}

/// Configuration options for *fd*.
struct FdOptions {
    /// Determines whether the regex search is case-sensitive or case-insensitive.
    case_sensitive: bool,

    /// Whether to search within the full file path or just the base name (filename or directory
    /// name).
    search_full_path: bool,

    /// Whether to ignore hidden files and directories (or not).
    ignore_hidden: bool,

    /// Whether to respect VCS ignore files (`.gitignore`, `.ignore`, ..) or not.
    read_ignore: bool,

    /// Whether to follow symlinks or not.
    follow_links: bool,

    /// Whether elements of output should be separated by a null character
    null_separator: bool,

    /// The maximum search depth, or `None` if no maximum search depth should be set.
    ///
    /// A depth of `1` includes all files under the current directory, a depth of `2` also includes
    /// all files under subdirectories of the current directory, etc.
    max_depth: Option<usize>,

    /// The number of threads to use.
    threads: usize,

    /// Time to buffer results internally before streaming to the console. This is useful to
    /// provide a sorted output, in case the total execution time is shorter than
    /// `max_buffer_time`.
    max_buffer_time: Option<time::Duration>,

    /// Display results as relative or absolute path.
    path_display: PathDisplay,

    /// `None` if the output should not be colorized. Otherwise, a `LsColors` instance that defines
    /// how to style different filetypes.
    ls_colors: Option<LsColors>
}

/// The receiver thread can either be buffering results or directly streaming to the console.
enum ReceiverMode {
    /// Receiver is still buffering in order to sort the results, if the search finishes fast
    /// enough.
    Buffering,

    /// Receiver is directly printing results to the output.
    Streaming
}

/// Root directory
static ROOT_DIR : &'static str = "/";

/// Parent directory
static PARENT_DIR : &'static str = "..";

/// Print a search result to the console.
fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
    let path_full = base.join(entry);

    let path_str = entry.to_string_lossy();

    #[cfg(target_family = "unix")]
    let is_executable = |p: &std::path::PathBuf| {
        p.metadata()
         .ok()
         .map(|f| f.permissions().mode() & 0o111 != 0)
         .unwrap_or(false)
    };

    #[cfg(not(target_family = "unix"))]
    let is_executable = |_: &std::path::PathBuf| { false };

    let stdout = std::io::stdout();
    let mut handle = stdout.lock();

    if let Some(ref ls_colors) = config.ls_colors {
        let default_style = ansi_term::Style::default();

        let mut component_path = base.to_path_buf();

        if config.path_display == PathDisplay::Absolute {
            print!("{}", ls_colors.directory.paint(ROOT_DIR));
        }

        // Traverse the path and colorize each component
        for component in entry.components() {
            let comp_str = match component {
                Component::Normal(p) => p.to_string_lossy(),
                Component::ParentDir => Cow::from(PARENT_DIR),
                _                    => error("Error: unexpected path component.")
            };

            component_path.push(Path::new(comp_str.deref()));

            let style =
                if component_path.symlink_metadata()
                                 .map(|md| md.file_type().is_symlink())
                                 .unwrap_or(false) {
                    &ls_colors.symlink
                } else if component_path.is_dir() {
                    &ls_colors.directory
                } else if is_executable(&component_path) {
                    &ls_colors.executable
                } else {
                    // Look up file name
                    let o_style =
                        component_path.file_name()
                                      .and_then(|n| n.to_str())
                                      .and_then(|n| ls_colors.filenames.get(n));

                    match o_style {
                        Some(s) => s,
                        None =>
                            // Look up file extension
                            component_path.extension()
                                          .and_then(|e| e.to_str())
                                          .and_then(|e| ls_colors.extensions.get(e))
                                          .unwrap_or(&default_style)
                    }
                };

            write!(handle, "{}", style.paint(comp_str)).ok();

            if component_path.is_dir() && component_path != path_full {
                let sep = std::path::MAIN_SEPARATOR.to_string();
                write!(handle, "{}", style.paint(sep)).ok();
            }
        }

        let r = if config.null_separator {
          write!(handle, "\0")
        } else {
          writeln!(handle, "")
        };
        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    } else {
        // Uncolorized output

        let prefix = if config.path_display == PathDisplay::Absolute { ROOT_DIR } else { "" };
        let separator = if config.null_separator { "\0" } else { "\n" };

        let r = write!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator);

        if r.is_err() {
            // Probably a broken pipe. Exit gracefully.
            process::exit(0);
        }
    }
}

/// Recursively scan the given search path and search for files / pathnames matching the pattern.
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
    let (tx, rx) = channel();

    let walker = WalkBuilder::new(root)
                     .hidden(config.ignore_hidden)
                     .ignore(config.read_ignore)
                     .git_ignore(config.read_ignore)
                     .parents(config.read_ignore)
                     .git_global(config.read_ignore)
                     .git_exclude(config.read_ignore)
                     .follow_links(config.follow_links)
                     .max_depth(config.max_depth)
                     .threads(config.threads)
                     .build_parallel();

    // Spawn the thread that receives all results through the channel.
    let rx_config = Arc::clone(&config);
    let rx_base = base.to_owned();
    let receiver_thread = thread::spawn(move || {
        let start = time::Instant::now();

        let mut buffer = vec!();

        // Start in buffering mode
        let mut mode = ReceiverMode::Buffering;

        // Maximum time to wait before we start streaming to the console.
        let max_buffer_time = rx_config.max_buffer_time
                                       .unwrap_or_else(|| time::Duration::from_millis(100));

        for value in rx {
            match mode {
                ReceiverMode::Buffering => {
                    buffer.push(value);

                    // Have we reached the maximum time?
                    if time::Instant::now() - start > max_buffer_time {
                        // Flush the buffer
                        for v in &buffer {
                            print_entry(&rx_base, v, &rx_config);
                        }
                        buffer.clear();

                        // Start streaming
                        mode = ReceiverMode::Streaming;
                    }
                },
                ReceiverMode::Streaming => {
                    print_entry(&rx_base, &value, &rx_config);
                }
            }
        }

        // If we have finished fast enough (faster than max_buffer_time), we haven't streamed
        // anything to the console, yet. In this case, sort the results and print them:
        if !buffer.is_empty() {
            buffer.sort();
            for value in buffer {
                print_entry(&rx_base, &value, &rx_config);
            }
        }
    });

    // Spawn the sender threads.
    walker.run(|| {
        let base = base.to_owned();
        let config = Arc::clone(&config);
        let pattern = Arc::clone(&pattern);
        let tx_thread = tx.clone();

        Box::new(move |entry_o| {
            let entry = match entry_o {
                Ok(e) => e,
                Err(_) => return ignore::WalkState::Continue
            };

            let path_rel_buf = match fshelper::path_relative_from(entry.path(), &*base) {
                Some(p) => p,
                None => error("Error: could not get relative path for directory entry.")
            };
            let path_rel = path_rel_buf.as_path();

            let search_str_o =
                if config.search_full_path {
                    Some(path_rel.to_string_lossy())
                } else {
                    path_rel.file_name()
                            .map(|f| f.to_string_lossy())
                };

            if let Some(search_str) = search_str_o {
                // TODO: take care of the unwrap call
                pattern.find(&*search_str)
                       .map(|_| tx_thread.send(path_rel_buf.to_owned()).unwrap());
            }

            ignore::WalkState::Continue
        })
    });

    // Drop the initial sender. If we don't do this, the receiver will block even
    // if all threads have finished, since there is still one sender around.
    drop(tx);

    // Wait for the receiver thread to print out all results.
    receiver_thread.join().unwrap();
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let matches =
        App::new("fd")
            .version(crate_version!())
            .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
            .setting(AppSettings::ColoredHelp)
            .setting(AppSettings::DeriveDisplayOrder)
            .arg(Arg::with_name("case-sensitive")
                        .long("case-sensitive")
                        .short("s")
                        .help("Case-sensitive search (default: smart case)"))
            .arg(Arg::with_name("full-path")
                        .long("full-path")
                        .short("p")
                        .help("Search full path (default: file-/dirname only)"))
            .arg(Arg::with_name("hidden")
                        .long("hidden")
                        .short("H")
                        .help("Search hidden files and directories"))
            .arg(Arg::with_name("no-ignore")
                        .long("no-ignore")
                        .short("I")
                        .help("Do not respect .(git)ignore files"))
            .arg(Arg::with_name("follow")
                        .long("follow")
                        .short("L")
                        .help("Follow symlinks")
                        .alias("dereference"))
            .arg(Arg::with_name("null_separator")
                        .long("print0")
                        .short("0")
                        .help("Separate results by the null character"))
            .arg(Arg::with_name("absolute-path")
                        .long("absolute-path")
                        .short("a")
                        .help("Show absolute instead of relative paths"))
            .arg(Arg::with_name("no-color")
                        .long("no-color")
                        .short("n")
                        .help("Do not colorize output"))
            .arg(Arg::with_name("depth")
                        .long("max-depth")
                        .short("d")
                        .takes_value(true)
                        .help("Set maximum search depth (default: none)"))
            .arg(Arg::with_name("threads")
                        .long("threads")
                        .short("j")
                        .takes_value(true)
                        .help("The number of threads used for searching"))
            .arg(Arg::with_name("max-buffer-time")
                        .long("max-buffer-time")
                        .takes_value(true)
                        .help("the time (in ms) to buffer, before streaming to the console")
                        .hidden(true))
            .arg(Arg::with_name("pattern")
                        .help("the search pattern, a regular expression (optional)"))
            .arg(Arg::with_name("path")
                        .help("the root directory for the filesystem search (optional)"))
            .get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Error: could not get current directory.")
    };
    let current_dir = current_dir_buf.as_path();

    // Get the root directory for the search
    let mut root_dir_is_absolute = false;
    let root_dir_buf = if let Some(rd) = matches.value_of("path") {
        let path = Path::new(rd);

        root_dir_is_absolute = path.is_absolute();

        path.canonicalize().unwrap_or_else(
            |_| error(&format!("Error: could not find directory '{}'.", rd))
        )
    } else {
        current_dir_buf.clone()
    };

    if !root_dir_buf.is_dir() {
        error(&format!("Error: '{}' is not a directory.", root_dir_buf.to_string_lossy()));
    }

    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = matches.is_present("case-sensitive") ||
                         pattern.chars().any(char::is_uppercase);

    let colored_output = !matches.is_present("no-color") &&
                         atty::is(Stream::Stdout);

    let ls_colors =
        if colored_output {
            Some(
                env::var("LS_COLORS")
                    .ok()
                    .map(|val| LsColors::from_string(&val))
                    .unwrap_or_default()
            )
        } else {
            None
        };

    let config = FdOptions {
        case_sensitive:    case_sensitive,
        search_full_path:  matches.is_present("full-path"),
        ignore_hidden:     !matches.is_present("hidden"),
        read_ignore:       !matches.is_present("no-ignore"),
        follow_links:      matches.is_present("follow"),
        null_separator:    matches.is_present("null_separator"),
        max_depth:         matches.value_of("depth")
                                   .and_then(|n| usize::from_str_radix(n, 10).ok()),
        threads:           std::cmp::max(
                             matches.value_of("threads")
                                    .and_then(|n| usize::from_str_radix(n, 10).ok())
                                    .unwrap_or_else(num_cpus::get),
                             1
                           ),
        max_buffer_time:   matches.value_of("max-buffer-time")
                                  .and_then(|n| u64::from_str_radix(n, 10).ok())
                                  .map(time::Duration::from_millis),
        path_display:      if matches.is_present("absolute-path") || root_dir_is_absolute {
                               PathDisplay::Absolute
                           } else {
                               PathDisplay::Relative
                           },
        ls_colors:         ls_colors
    };

    let root = Path::new(ROOT_DIR);
    let base = match config.path_display {
        PathDisplay::Relative => current_dir,
        PathDisplay::Absolute => root
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(root_dir, Arc::new(re), base, Arc::new(config)),
        Err(err) => error(err.description())
    }
}
<|endoftext|>


<commit_before>/*!
Treesum calculates check|sums| for files in directory |tree|s. The successful
results are written to stdout while errors and status messages are written to
stderr. Example output:

```
01010101010101010101	root/some_subdir/cat.gif
12121212121212121212	root/I Like Space In My Paths/HerpDerp Final 3 (17).xlsx
```
*/
#![deny(warnings)]

extern crate crypto;
extern crate sgiter;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io;
use std::path::Path;
use walkdir::DirEntry;
use walkdir::WalkDir;

/// Calculate a checksum for a file.
fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn print_success(t: (DirEntry, String)) {
    println!("{}\t{}", t.1, t.0.path().display());
}

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

/// Helper for handling error reporting levels and printing.
/// TODO: Use stderr. Find crate?
fn print_err(e: Error, verbose: bool) {
    match e {
        Error::Ignored(ent) => {
            if verbose {
                println!("ignored: {}", ent.path().display())
            }
        }
        _ => println!("ERROR: {:?}", e),
    }
}

type ResultIter = sgiter::GatherIter<Result<(DirEntry, String), Error>>;

/// Allocates memory for and collects all successfull hashes before sorting and
/// then printing. Errors are printed immediately.
fn do_sorted_output(res:ResultIter, verbose:bool) {
    let mut tuples = vec![];
    for r in res {
        match r {
            Ok(t)  => tuples.push(t),
            Err(e) => print_err(e, verbose),
        }
    }
    tuples.sort_by(|a, b| a.1.cmp(&b.1));
    for t in tuples {
        print_success(t);
    }
}

/// Prints both errors and successful hashes immediately without sorting.
fn do_unsorted_output(res:ResultIter, verbose:bool) {
    for r in res {
        match r {
            Ok(t)  => print_success(t),
            Err(e) => print_err(e, verbose),
        }
    }
}

/// Bind constructors for producer and consumers, start scatter gather and
/// pass on result iterator to handler for either sorted or unsorted output.
fn process_root(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let producer_ctor = || WalkDir::new(pb);
    let xform_ctor = || {
        let mut hasher = Sha1::new();
        let mut buf = [0; 1024 * 8];
        let f = move |e: walkdir::Result<DirEntry>| {
            let e = e.map_err(Error::WalkDir)?;
            if !e.file_type().is_file() {
                return Err(Error::Ignored(e));
            }
            calc_hash(e.path(), &mut hasher, &mut buf)
                .map_err(Error::Io)
                .map(|s| (e, s))
        };
        f
    };
    let results = sgiter::scatter_gather(producer_ctor, xform_ctor);
    let verbose = false;       // TODO: add command line flag
    let sort_successes = true; // TODO: add command line flag
    if sort_successes {
        do_sorted_output(results, verbose)
    } else {
        do_unsorted_output(results, verbose)
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    process_root(root).unwrap()
}

<commit_msg>rustfmt.<commit_after>/*!
Treesum calculates check|sums| for files in directory |tree|s. The successful
results are written to stdout while errors and status messages are written to
stderr. Example output:

```
01010101010101010101	root/some_subdir/cat.gif
12121212121212121212	root/I Like Space In My Paths/HerpDerp Final 3 (17).xlsx
```
*/
#![deny(warnings)]

extern crate crypto;
extern crate sgiter;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io;
use std::path::Path;
use walkdir::DirEntry;
use walkdir::WalkDir;

/// Calculate a checksum for a file.
fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn print_success(t: (DirEntry, String)) {
    println!("{}\t{}", t.1, t.0.path().display());
}

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

/// Helper for handling error reporting levels and printing.
/// TODO: Use stderr. Find crate?
fn print_err(e: Error, verbose: bool) {
    match e {
        Error::Ignored(ent) => {
            if verbose {
                println!("ignored: {}", ent.path().display())
            }
        }
        _ => println!("ERROR: {:?}", e),
    }
}

type ResultIter = sgiter::GatherIter<Result<(DirEntry, String), Error>>;

/// Allocates memory for and collects all successfull hashes before sorting and
/// then printing. Errors are printed immediately.
fn do_sorted_output(res: ResultIter, verbose: bool) {
    let mut tuples = vec![];
    for r in res {
        match r {
            Ok(t) => tuples.push(t),
            Err(e) => print_err(e, verbose),
        }
    }
    tuples.sort_by(|a, b| a.1.cmp(&b.1));
    for t in tuples {
        print_success(t);
    }
}

/// Prints both errors and successful hashes immediately without sorting.
fn do_unsorted_output(res: ResultIter, verbose: bool) {
    for r in res {
        match r {
            Ok(t) => print_success(t),
            Err(e) => print_err(e, verbose),
        }
    }
}

/// Bind constructors for producer and consumers, start scatter gather and
/// pass on result iterator to handler for either sorted or unsorted output.
fn process_root(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let producer_ctor = || WalkDir::new(pb);
    let xform_ctor = || {
        let mut hasher = Sha1::new();
        let mut buf = [0; 1024 * 8];
        let f = move |e: walkdir::Result<DirEntry>| {
            let e = e.map_err(Error::WalkDir)?;
            if !e.file_type().is_file() {
                return Err(Error::Ignored(e));
            }
            calc_hash(e.path(), &mut hasher, &mut buf)
                .map_err(Error::Io)
                .map(|s| (e, s))
        };
        f
    };
    let results = sgiter::scatter_gather(producer_ctor, xform_ctor);
    let verbose = false; // TODO: add command line flag
    let sort_successes = true; // TODO: add command line flag
    if sort_successes {
        do_sorted_output(results, verbose)
    } else {
        do_unsorted_output(results, verbose)
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    process_root(root).unwrap()
}
<|endoftext|>


<commit_before>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    for argument in env::args() {
        println!("{}", argument);
    }

    let vm = VM::new();
    let program = Program::new();
    match env::args().nth(1) {
        None => repl(vm, program),
        Some(arg) => interpreter(arg, vm, program),
    }
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}

fn interpreter(arg: String, mut vm: VM, mut program: Program) -> () {
    let path = Path::new(&arg);
    let mut contents = String::new();
    File::open(&path)
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    program.append(&parse::parse(&contents));
    program.execute(&mut vm);
}
<commit_msg>Prevent printing out of arguments<commit_after>mod vm;
mod command;
mod parse;
mod program;

use vm::VM;
use program::Program;
use std::io::Write;
use std::env;
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let vm = VM::new();
    let program = Program::new();
    match env::args().nth(1) {
        None => repl(vm, program),
        Some(arg) => interpreter(arg, vm, program),
    }
}

fn repl (mut vm: VM, mut program: Program) -> () {
    loop {
        print!("{}", if program.is_seeking {"... "} else {"bf> "});
        std::io::stdout().flush().unwrap();

        let mut line = String::new();
        let commands = match std::io::stdin().read_line(&mut line) {
            Ok(0) => break,
            Ok(_) => parse::parse(&line),
            Err(error) => panic!(error),
        };
        program.append(&commands);
        program.execute(&mut vm);
    }
}

fn interpreter(arg: String, mut vm: VM, mut program: Program) -> () {
    let path = Path::new(&arg);
    let mut contents = String::new();
    File::open(&path)
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    program.append(&parse::parse(&contents));
    program.execute(&mut vm);
}
<|endoftext|>


<commit_before>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) -> Result<(),git2::Error> {

  let tmp_repo = try!(Repository::init_bare(TMP_REPO_DIR));
  let _ = in_tmp_repo("fetch --all");

  transfer_to_tmp(newrev);
  let parent_commit_obj = try!(tmp_repo.revparse_single(CENTRAL_NAME));
  let mut parent_commit_oid: git2::Oid = try!(parent_commit_obj
                                              .as_commit()
                                              .map(|x| x.id())
                                              .ok_or(git2::Error::from_str("could not get commit from obj")));

  let module_name: &str = try!(Path::new(project)
                               .components()
                               .last()
                               .map(|x| x.as_os_str().to_str().expect("not a valid unicode string"))
                               .ok_or(git2::Error::from_str("needs to be valid name")));
  let module_path = format!("remotes/modules/{}/master", module_name);
  let object = try!(tmp_repo.revparse_single(&module_path).map(|x| x.id()));
  let oldrev = format!("{}", object);

  {
    let old = try!(tmp_repo.revparse_single(&oldrev).map(|x| x.id()));
    let new = try!(tmp_repo.revparse_single(&newrev).map(|x| x.id()));

    if !try!(tmp_repo.graph_descendant_of(new,old)) {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return Ok(());
    }
  }

  let walk = {
    let mut walk = try!(tmp_repo.revwalk());
    walk.set_sorting(git2::SORT_REVERSE | git2::SORT_TIME);
    try!(walk.push_range(&format!("{}..{}", oldrev, newrev)));
    walk
  };

  println!("===== project path: {}", project);
  println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}", try!(rev));
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = try!(tmp_repo.revparse_single(&newrev));
    let module_commit = try!(module_commit_obj.as_commit()
                             .ok_or(git2::Error::from_str("object is not actually a commit")));
    let module_tree = try!(module_commit.tree());

    let parent_commit = try!(tmp_repo.find_commit(parent_commit_oid));

    let new_tree = {
      let master_tree: Tree = try!(parent_commit.tree());
      let new_tree_oid = try!(module_to_subfolder(
          Path::new(module_name),
          &module_tree,
          &master_tree));
      try!(tmp_repo.find_tree(new_tree_oid))
    };

    parent_commit_oid = try!(make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)));

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let parent_commit = try!(tmp_repo.find_commit(parent_commit_oid));
  let x = try!(push_from_tmp(
      &tmp_repo,
      &parent_commit,
      CENTRAL_NAME,
      "refs/for/master"
      ));
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
  Ok(())
}

fn central_submit(newrev: &str) {
  println!("central_submit");

  let module_names = get_module_names(newrev).unwrap();
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let _ = tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    let _ = tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
        ).unwrap();
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
    .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
    .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
    .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> Result<String, git2::Error> {
  let args: Vec<&str> = cmd.split(" ").collect();
  match Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().map(|output|
                              format!("{}", String::from_utf8_lossy(&output.stderr))) {
      Ok(value) => Ok(value),
      Err(_) => Err(git2::Error::from_str("could not fire git command")),
    }
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    let _ = repo.remote("central_repo",
                        &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
                       );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
      .arg("gerrit")
      .arg("create-project")
      .arg(format!("bsw/modules/{}",module))
      .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
                             AUTOMATION_USER,
                             module
                            );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
      let _ = repo.remote(&remote_name, &remote_url);
    }
  }

  let _ = in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Result<Oid, git2::Error> {
  let mpath = Path::new("modules");
  let modules_oid = try!(master_tree.get_path(mpath).map(|x| x.id()));
  let tmp_repo = try!(Repository::init_bare(TMP_REPO_DIR));

  let modules_tree = try!(tmp_repo.find_tree(modules_oid));
  let mut mbuilder = try!(tmp_repo.treebuilder(Some(&modules_tree)));
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = try!(mbuilder.write());

  let mut builder = try!(tmp_repo.treebuilder(Some(master_tree)));
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = try!(builder.write());
  println!("module_to_subfolder {}", r);
  Ok(r)
}

fn get_module_names(rev: &str) -> Result<Vec<String>, git2::Error> {
  let central_repo = try!(Repository::open("."));

  let object = try!(central_repo.revparse_single(rev));
  let commit = try!(object
                    .as_commit()
                    .ok_or(git2::Error::from_str("could not get commit from obj")));
  let tree: git2::Tree = try!(commit.tree());

  let modules_o = try!(tree.get_path(&Path::new("modules")).unwrap()
                   .to_object(&central_repo));
  let modules = try!(modules_o.as_tree()
                    .ok_or(git2::Error::from_str("could not get tree from path")));

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  Ok(names)
}

fn push_from_tmp(tmp_repo: &Repository,
                 commit: &Commit,
                 repo: &str ,to: &str)
  -> Result<String, git2::Error> {
    let _ = tmp_repo.set_head_detached(commit.id());
    in_tmp_repo(
      &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
               AUTOMATION_USER,
               repo,
               to
              )
      )
  }

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Result<Oid, git2::Error> {
  if parents.len() != 0 {
    let _ = repo.set_head_detached(parents[0].id());
  }
  repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
    )
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { let _ = module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<commit_msg>improved error handling<commit_after>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) -> Result<(),git2::Error> {

  let tmp_repo = try!(Repository::init_bare(TMP_REPO_DIR));
  try!(in_tmp_repo("fetch --all"));

  transfer_to_tmp(newrev);
  let mut parent_commit_oid: git2::Oid =
    try!(try!(tmp_repo.revparse_single(CENTRAL_NAME))
         .as_commit()
         .map(|x| x.id())
         .ok_or(git2::Error::from_str("could not get commit from obj")));

  let module_name: &str = try!(Path::new(project)
                               .components()
                               .last()
                               .map(|x| x.as_os_str().to_str().expect("not a valid unicode string"))
                               .ok_or(git2::Error::from_str("needs to be valid name")));
  let module_path = format!("remotes/modules/{}/master", module_name);
  let object = try!(tmp_repo.revparse_single(&module_path).map(|x| x.id()));
  let oldrev = format!("{}", object);

  {
    let old = try!(tmp_repo.revparse_single(&oldrev).map(|x| x.id()));
    let new = try!(tmp_repo.revparse_single(&newrev).map(|x| x.id()));

    if !try!(tmp_repo.graph_descendant_of(new,old)) {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return Ok(());
    }
  }

  let walk = {
    let mut walk = try!(tmp_repo.revwalk());
    walk.set_sorting(git2::SORT_REVERSE | git2::SORT_TIME);
    try!(walk.push_range(&format!("{}..{}", oldrev, newrev)));
    walk
  };

  println!("===== project path: {}", project);
  println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}", try!(rev));
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = try!(tmp_repo.revparse_single(&newrev));
    let module_commit = try!(module_commit_obj.as_commit()
                             .ok_or(git2::Error::from_str("object is not actually a commit")));
    let module_tree = try!(module_commit.tree());

    let parent_commit = try!(tmp_repo.find_commit(parent_commit_oid));

    let new_tree = {
      let master_tree: Tree = try!(parent_commit.tree());
      let new_tree_oid = try!(module_to_subfolder(
          Path::new(module_name),
          &module_tree,
          &master_tree));
      try!(tmp_repo.find_tree(new_tree_oid))
    };

    parent_commit_oid = try!(make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)));
  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = try!(push_from_tmp(
      &tmp_repo,
      &try!(tmp_repo.find_commit(parent_commit_oid)),
      CENTRAL_NAME,
      "refs/for/master"
      ));
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
  Ok(())
}

fn central_submit(newrev: &str) -> Result<(), git2::Error> {
  println!("central_submit");

  let module_names = try!(get_module_names(newrev));
  let tmp_repo = try!(setup_tmp_repo(&module_names));
  transfer_to_tmp(newrev);

  let central_commit_obj = try!(tmp_repo.revparse_single(newrev));
  let central_commit = try!(central_commit_obj.as_commit()
                            .ok_or(git2::Error::from_str("could not get commit from obj")));
  let central_tree = try!(central_commit.tree());
  try!(tmp_repo.branch(CENTRAL_NAME,central_commit,true));

  for module_name in module_names {
    let module_master_commit_obj =
      try!(tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)));
    let module_master_commit =
      try!(module_master_commit_obj.as_commit()
           .ok_or(git2::Error::from_str("could not get commit from obj")));
    try!(tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true));

    let parents = vec!(module_master_commit);
    let old_tree_oid = try!(module_master_commit.tree()).id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = try!(central_tree.get_path(&module_path)).id();

    if new_tree_oid != old_tree_oid {

      let new_tree = try!(tmp_repo.find_tree(new_tree_oid));

      let module_commit = try!(make_commit(&tmp_repo, &new_tree, central_commit, &parents));
      let x = try!(push_from_tmp(
          &tmp_repo,
          &try!(tmp_repo.find_commit(module_commit)),
          &format!("bsw/modules/{}",module_name),
          "master"
          ));
      println!("{}", x);
    }
  }
  Ok(())
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
    .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
    .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
    .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> Result<String, git2::Error> {
  let args: Vec<&str> = cmd.split(" ").collect();
  match Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().map(|output|
                              format!("{}", String::from_utf8_lossy(&output.stderr))) {
      Ok(value) => Ok(value),
      Err(_) => Err(git2::Error::from_str("could not fire git command")),
    }
}

fn setup_tmp_repo(modules: &Vec<String>) -> Result<Repository, git2::Error> {
  let repo = try!(Repository::init_bare(TMP_REPO_DIR));

  if !repo.find_remote("central_repo").is_ok() {
    try!(repo.remote("central_repo",
                     &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
                    ));
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
      .arg("gerrit")
      .arg("create-project")
      .arg(format!("bsw/modules/{}",module))
      .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
                             AUTOMATION_USER,
                             module
                            );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
      try!(repo.remote(&remote_name, &remote_url));
    }
  }

  try!(in_tmp_repo("fetch --all"));

  Ok(repo)
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Result<Oid, git2::Error> {
  let mpath = Path::new("modules");
  let modules_oid = try!(master_tree.get_path(mpath).map(|x| x.id()));
  let tmp_repo = try!(Repository::init_bare(TMP_REPO_DIR));

  let modules_tree = try!(tmp_repo.find_tree(modules_oid));
  let mut mbuilder = try!(tmp_repo.treebuilder(Some(&modules_tree)));
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = try!(mbuilder.write());

  let mut builder = try!(tmp_repo.treebuilder(Some(master_tree)));
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = try!(builder.write());
  println!("module_to_subfolder {}", r);
  Ok(r)
}

fn get_module_names(rev: &str) -> Result<Vec<String>, git2::Error> {
  let central_repo = try!(Repository::open("."));

  let object = try!(central_repo.revparse_single(rev));
  let commit = try!(object
                    .as_commit()
                    .ok_or(git2::Error::from_str("could not get commit from obj")));
  let tree: git2::Tree = try!(commit.tree());

  let tree_object = try!(tree.get_path(&Path::new("modules")));
  let modules_o = try!(tree_object.to_object(&central_repo));
  let modules = try!(modules_o.as_tree()
                     .ok_or(git2::Error::from_str("could not get tree from path")));

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(try!(module
                    .name()
                    .ok_or(git2::Error::from_str("could not get name for module")))
               .to_string());
  }
  Ok(names)
}

fn push_from_tmp(tmp_repo: &Repository,
                 commit: &Commit,
                 repo: &str ,to: &str)
  -> Result<String, git2::Error> {
    try!(tmp_repo.set_head_detached(commit.id()));
    in_tmp_repo(
      &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
               AUTOMATION_USER,
               repo,
               to
              )
      )
  }

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Result<Oid, git2::Error> {
  if parents.len() != 0 {
    try!(repo.set_head_detached(parents[0].id()));
  }
  repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
    )
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit).unwrap(); }
    else if !is_module && is_update && !is_review { central_submit(newrev).unwrap(); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev).unwrap(); return 1; }
  }

  return 0;
}

<|endoftext|>


<commit_before>extern crate sdl2;
extern crate sdl2_ttf;
extern crate sdl2_engine_helpers;
extern crate uorustlibs;

mod engine;
mod scene;
mod title_scene;
mod skills_scene;
mod text_renderer;

use std::path::Path;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::render::Renderer;
use sdl2_engine_helpers::game_loop::GameLoop;

pub fn make_scene(scene: scene::SceneName, text_renderer: &text_renderer::TextRenderer, renderer: &mut Renderer) -> scene::BoxedScene<scene::SceneName> {
    match scene {
        scene::SceneName::TitleScene => {
            title_scene::TitleScene::new(text_renderer, renderer)
        },
        scene::SceneName::SkillsScene => {
            skills_scene::SkillsScene::new(text_renderer, renderer)
        },
    }
}

pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let ttf_subsystem = sdl2_ttf::init().unwrap();

    let window = video_subsystem.window("UO Data Viewer", 1024, 768)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();
    let font_path = Path::new("./assets/Bretan.otf");
    let font = ttf_subsystem.load_font(font_path, 16).unwrap();
    let text_renderer = text_renderer::TextRenderer::new(font);

    let mut event_pump = sdl_context.event_pump().unwrap();
    let mut engine = engine::Engine::new(30, event_pump, title_scene::TitleScene::new(&text_renderer, &mut renderer));

    engine.run(|scene, renderer| {
    }, &mut renderer);

    /*game_loop.run(|frame| {
        let mut ended = false;
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..}  => {
                    ended = true
                },
                _ => {
                    let scene_event = scene_stack.handle_event(&event);
                    match scene_event {
                        Some(scene::SceneChangeEvent::PopScene) => {
                            scene_stack.pop();
                        },
                        Some(scene::SceneChangeEvent::PushScene(scene)) => {
                            scene_stack.push(make_scene(scene, &text_renderer, &mut renderer))
                        },
                        Some(scene::SceneChangeEvent::SwapScene(scene)) => {
                            scene_stack.pop();
                            scene_stack.push(make_scene(scene, &text_renderer, &mut renderer))
                        },
                        _ => ()
                    }
                }
            }
        }
        scene_stack.render(&mut renderer);
        ended || scene_stack.is_empty()
    });*/
}
<commit_msg>Now using Engine handler<commit_after>extern crate sdl2;
extern crate sdl2_ttf;
extern crate sdl2_engine_helpers;
extern crate uorustlibs;

mod engine;
mod scene;
mod title_scene;
mod skills_scene;
mod text_renderer;

use std::path::Path;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::render::Renderer;
use sdl2_engine_helpers::game_loop::GameLoop;

pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let ttf_subsystem = sdl2_ttf::init().unwrap();

    let window = video_subsystem.window("UO Data Viewer", 1024, 768)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();
    let font_path = Path::new("./assets/Bretan.otf");
    let font = ttf_subsystem.load_font(font_path, 16).unwrap();
    let text_renderer = text_renderer::TextRenderer::new(font);

    let mut event_pump = sdl_context.event_pump().unwrap();
    let mut engine = engine::Engine::new(30, event_pump, title_scene::TitleScene::new(&text_renderer, &mut renderer));

    engine.run(|scene, renderer| {
        match scene {
            scene::SceneName::TitleScene => {
                title_scene::TitleScene::new(&text_renderer, renderer)
            },
            scene::SceneName::SkillsScene => {
                skills_scene::SkillsScene::new(&text_renderer, renderer)
            },
        }
    }, &mut renderer);
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

enum ParseAtomError {
    IncorrectSymbolName
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom::Number(n) => {
                write!(f, "{}", n)
            },
            Atom::Symbol(ref name) => {
                write!(f, "'{}", name)
            }
        }
    }
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            Err(..) => {
                match s.chars().next() {
                    Some(c) => {
                        if c.is_numeric() {
                            Err(ParseAtomError::IncorrectSymbolName)
                        } else {
                            Ok(Atom::Symbol(s.to_string()))
                        }
                    },
                    None => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize<'a>(s: &'a str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter_map(|s| { if s.is_empty() { None } else { Some(s.to_string()) }})
        .collect()
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod test {
    use super::{Atom, tokenize};

    #[test]
    fn parse_integer() {
        assert_eq!(Atom::Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn parse_float() {
        assert_eq!(Atom::Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn parse_symbol() {
        assert_eq!(Atom::Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn tokenize_dense_expression() {
        let expected_result = vec!("(", "def", "a", "1", ")")
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn tokenize_sparse_expression() {
        let expected_result = vec!("(", "def", "a", "1", ")")
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }
}
<commit_msg>Remove unnecessary liftime declaration<commit_after>use std::fmt;
use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

enum ParseAtomError {
    IncorrectSymbolName
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom::Number(n) => {
                write!(f, "{}", n)
            },
            Atom::Symbol(ref name) => {
                write!(f, "'{}", name)
            }
        }
    }
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            Err(..) => {
                match s.chars().next() {
                    Some(c) => {
                        if c.is_numeric() {
                            Err(ParseAtomError::IncorrectSymbolName)
                        } else {
                            Ok(Atom::Symbol(s.to_string()))
                        }
                    },
                    None => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter_map(|s| { if s.is_empty() { None } else { Some(s.to_string()) }})
        .collect()
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod test {
    use super::{Atom, tokenize};

    #[test]
    fn parse_integer() {
        assert_eq!(Atom::Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn parse_float() {
        assert_eq!(Atom::Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn parse_symbol() {
        assert_eq!(Atom::Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn tokenize_dense_expression() {
        let expected_result = vec!("(", "def", "a", "1", ")")
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn tokenize_sparse_expression() {
        let expected_result = vec!("(", "def", "a", "1", ")")
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }
}
<|endoftext|>


<commit_before>#![feature(old_io, io, path, std_misc, os, exit_status)]
#![cfg_attr(target_os = "linux", feature(libc))]
extern crate zaldinar;
extern crate getopts;
#[cfg(target_os = "linux")]
extern crate libc;

use std::env;
use std::io;
use std::old_io::stdio;
use std::ffi::AsOsStr;
#[cfg(target_os = "linux")]
use std::os::unix::OsStrExt;
use std::path::PathBuf;
use std::path::Path;
#[cfg(target_os = "linux")]
use std::ffi;
#[cfg(target_os = "linux")]
use libc::funcs::posix88::unistd;
#[cfg(target_os = "linux")]
use std::ptr;

const UNKNOWN_EXECUTABLE: &'static str = "<unknown executable>";

macro_rules! print_err {
    ($($arg:tt)*) => (
        if let Err(e) = write!(&mut stdio::stderr_raw(), $($arg)*) {
            panic!("Failed to write to stderr.\
                \nOriginal error output: {}\
                \nSecondary error writing to stderr: {}", format!($($arg)*), e);
        }
    )
}

#[cfg(target_os = "linux")]
#[inline]
fn execv_if_possible(program_path: &Path) {
    if program_path == Path::new(UNKNOWN_EXECUTABLE) {
        print_err!("Couldn't restart using exec: executable unknown! See previous \"failed to \
                    find current executable\" error.");
        env::set_exit_status(1);
        return;
    }

    // We're just going to exit the program anyways if we succeed or fail, so this function won't do anything other than unwrap IO errors.

    let program = program_path.as_os_str().to_cstring().unwrap();
    let mut argv_vec = Vec::new();
    for arg in env::args().skip(1) {
        argv_vec.push(ffi::CString::new(arg).unwrap().as_ptr());
    }
    argv_vec.push(ptr::null());

    println!("Executing `{:?}`", program_path);

    unsafe {
        unistd::execv(program.as_ptr(), argv_vec.as_mut_ptr());
    }
    println!("Oh hi, executing didn't work.");
    unsafe {
        unistd::execv(ffi::CString::new("/bin/bash").unwrap().as_ptr(), vec!().as_mut_ptr());
    }
    println!("Oh hi, executing didn't work *again*.");
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn execv_if_possible(_program_path: &Path) {
    println!("Can't execv! Invalid action for this platform.");
}

fn get_program() -> io::Result<PathBuf> {
    // This essentially joins current_dir() and current_exe(), resulting in an absolute path
    // to of the current executable
    let mut buf = try!(env::current_dir());
    buf.push(&try!(env::current_exe()));
    return Ok(buf);
}

fn main() {
    // Because env::current_exe() will change if the executing file is moved, we need to securely
    // get the original program path as soon as we start.
    // We have two `program` variables because we still want to use the program gotten from
    // env::args() to print help strings.
    let original_program = match get_program() {
        Ok(v) => v,
        Err(e) => {
            print_err!("Warning: failed to find current executable: {}", e);
            PathBuf::new(UNKNOWN_EXECUTABLE)
        }
    };

    let mut args = env::args();
    let display_program = match args.next() {
        // TODO: some error catching of lossy strings here or not?
        Some(v) => v,
        None => original_program.as_os_str().to_string_lossy().into_owned(),
    };
    let mut opts = getopts::Options::new();
    opts.optopt("c", "config", "set config file name", "FILE");
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print program version");

    // TODO: Bug getopts to use OsString
    let matches = match opts.parse(args.collect::<Vec<String>>()) {
        Ok(v) => v,
        Err(e) => {
            print_err!("{}", e.to_string());
            return;
        }
    };

    if matches.opt_present("help") {
        let brief = format!("Usage: {} [options]", display_program);
        println!("{}", opts.usage(brief.as_slice()));
        return;
    } else if matches.opt_present("version") {
        println!("zaldinar version {}", zaldinar::VERSION);
        return;
    }

    let current_dir = match env::current_dir() {
        Ok(v) => v,
        Err(e) => {
            print_err!("Warning: failed to get current directory: {}", e);
            PathBuf::new("") // TODO: return here or just not be absolute?
        }
    };

    let config_path = match matches.opt_str("config") {
        Some(v) => {
            let absolute = current_dir.join(&Path::new(&v));
            println!("Using configuration: {}", absolute.display());
            absolute
        },
        None => current_dir.join(Path::new("config.json")),
    };

    loop {
        let config = match zaldinar::ClientConfiguration::load_from_file(&config_path) {
            Ok(v) => v,
            Err(e) => {
                print_err!("Error loading configuration from `{}`: {}",config_path.display(), e);
                env::set_exit_status(1);
                return;
            },
        };

        match zaldinar::client::run(config) {
            Ok(zaldinar::client::ExecutingState::Done) => {
                println!("Done, exiting.");
                break
            },
            Ok(zaldinar::client::ExecutingState::Running) => {
                println!("Restarting zaldinar main loop.");
                continue;
            },
            Ok(zaldinar::client::ExecutingState::Restart) => {
                println!("Restarting zaldinar using exec.");
                execv_if_possible(&original_program);
                return;
            }
            Err(e) => {
                println!("Error running client: {}", e);
                env::set_exit_status(1);
            },
        };
    }
}
<commit_msg>Fix arguments passed during restart with execv - actually pass the program as an argument as well.<commit_after>#![feature(old_io, io, path, std_misc, os, exit_status)]
#![cfg_attr(target_os = "linux", feature(libc))]
extern crate zaldinar;
extern crate getopts;
#[cfg(target_os = "linux")]
extern crate libc;

use std::env;
use std::io;
use std::old_io::stdio;
use std::ffi::AsOsStr;
#[cfg(target_os = "linux")]
use std::os::unix::OsStrExt;
use std::path::PathBuf;
use std::path::Path;
#[cfg(target_os = "linux")]
use std::ffi;
#[cfg(target_os = "linux")]
use libc::funcs::posix88::unistd;
#[cfg(target_os = "linux")]
use std::ptr;

const UNKNOWN_EXECUTABLE: &'static str = "<unknown executable>";

macro_rules! print_err {
    ($($arg:tt)*) => (
        if let Err(e) = write!(&mut stdio::stderr_raw(), $($arg)*) {
            panic!("Failed to write to stderr.\
                \nOriginal error output: {}\
                \nSecondary error writing to stderr: {}", format!($($arg)*), e);
        }
    )
}

#[cfg(target_os = "linux")]
#[inline]
fn execv_if_possible(program_path: &Path) {
    if program_path == Path::new(UNKNOWN_EXECUTABLE) {
        print_err!("Couldn't restart using exec: executable unknown! See previous \"failed to \
                    find current executable\" error.");
        env::set_exit_status(1);
        return;
    }

    // We're just going to exit the program anyways if we succeed or fail, so this function won't do anything other than unwrap IO errors.

    // Get the program as a CString
    let program = program_path.as_os_str().to_cstring().unwrap();

    // Argument vector, passed to execv.
    let mut argv_vec = Vec::new();
    // Printable argument vector, used for printing arguments before executing.
    let mut printable_args = Vec::new();

    // We don't use skip(1) on env::args() here, because the execv() needs the first argument to
    // be the program, just like env::args().
    for arg in env::args() {
        // Just use &*arg so that printable_args can then have the ownership.
        argv_vec.push(ffi::CString::new(&*arg).unwrap().as_ptr());
        printable_args.push(arg);
    }
    // Push a null pointer so that argv_vec is null terminated for execv.
    argv_vec.push(ptr::null());

    println!("Executing `{:?}` (arguments: `{:?}`", program_path, printable_args);

    unsafe {
        unistd::execv(program.as_ptr(), argv_vec.as_mut_ptr());
    }
    println!("Executing using execv failed!");
    env::set_exit_status(1);
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn execv_if_possible(_program_path: &Path) {
    println!("Can't restart using execv on this platform!");
}

fn get_program() -> io::Result<PathBuf> {
    // This essentially joins current_dir() and current_exe(), resulting in an absolute path
    // to of the current executable
    let mut buf = try!(env::current_dir());
    buf.push(&try!(env::current_exe()));
    return Ok(buf);
}

fn main() {
    // Because env::current_exe() will change if the executing file is moved, we need to securely
    // get the original program path as soon as we start.
    // We have two `program` variables because we still want to use the program gotten from
    // env::args() to print help strings.
    let original_program = match get_program() {
        Ok(v) => v,
        Err(e) => {
            print_err!("Warning: failed to find current executable: {}", e);
            PathBuf::new(UNKNOWN_EXECUTABLE)
        }
    };

    let mut args = env::args();
    let display_program = match args.next() {
        // TODO: some error catching of lossy strings here or not?
        Some(v) => v,
        None => original_program.as_os_str().to_string_lossy().into_owned(),
    };
    let mut opts = getopts::Options::new();
    opts.optopt("c", "config", "set config file name", "FILE");
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print program version");

    // TODO: Bug getopts to use OsString
    let matches = match opts.parse(args.collect::<Vec<String>>()) {
        Ok(v) => v,
        Err(e) => {
            print_err!("{}", e.to_string());
            return;
        }
    };

    if matches.opt_present("help") {
        let brief = format!("Usage: {} [options]", display_program);
        println!("{}", opts.usage(brief.as_slice()));
        return;
    } else if matches.opt_present("version") {
        println!("zaldinar version {}", zaldinar::VERSION);
        return;
    }

    let current_dir = match env::current_dir() {
        Ok(v) => v,
        Err(e) => {
            print_err!("Warning: failed to get current directory: {}", e);
            PathBuf::new("") // TODO: return here or just not be absolute?
        }
    };

    let config_path = match matches.opt_str("config") {
        Some(v) => {
            let absolute = current_dir.join(&Path::new(&v));
            println!("Using configuration: {}", absolute.display());
            absolute
        },
        None => current_dir.join(Path::new("config.json")),
    };

    loop {
        let config = match zaldinar::ClientConfiguration::load_from_file(&config_path) {
            Ok(v) => v,
            Err(e) => {
                print_err!("Error loading configuration from `{}`: {}",config_path.display(), e);
                env::set_exit_status(1);
                return;
            },
        };

        match zaldinar::client::run(config) {
            Ok(zaldinar::client::ExecutingState::Done) => {
                println!("Done, exiting.");
                break
            },
            Ok(zaldinar::client::ExecutingState::Running) => {
                println!("Restarting zaldinar main loop.");
                continue;
            },
            Ok(zaldinar::client::ExecutingState::Restart) => {
                println!("Restarting zaldinar using exec.");
                execv_if_possible(&original_program);
                return;
            }
            Err(e) => {
                println!("Error running client: {}", e);
                env::set_exit_status(1);
            },
        };
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};

// Bytecode instruction opcodes. The values of these opcodes should never
// change, to remain compatible with existing bytecode programs.
//
// To make the from_u8 function work, MAX_INST_VARIANT must be kept in sync with
// this enum and every value from 0 to MAX_INST_VARIANT should be assigned to an
// opcode.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
enum Inst {
    Nop   = 0,
    Push  = 1,
    Dup   = 2,
    Pop   = 3,
    Swap  = 4,
    Add   = 5,
    Print = 6,
    Halt  = 7,
}
const MAX_INST_VARIANT: u8 = 7;

impl Inst {
    fn from_u8(x: u8) -> Option<Self> {
        if x <= MAX_INST_VARIANT {
            // This is safe as long as MAX_INST_VARIANT
            Some(unsafe { std::mem::transmute::<u8, Self>(x) })
        } else {
            None
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum VmError {
    InvalidOpcode,
    UnexpectedProgramEnd, // Hit end of program while reading opcode argument.
    StackOverflow,
    StackUnderflow,
}

fn execute(program: &[u8],
           stack: &mut [i32],
           mut stack_idx: usize) -> Result<usize, VmError> {
    use VmError::*;

    let stack_size = stack.len();
    let mut opcodes = Cursor::new(program);

    while let Ok(opcode) = opcodes.read_u8() {
        let inst = try!(Inst::from_u8(opcode).ok_or(InvalidOpcode));
        match inst {
            Inst::Nop => {},

            Inst::Push => {
                let val = try!(opcodes.read_i32::<LittleEndian>().or(Err(UnexpectedProgramEnd)));
                let stack_top = try!(stack.get_mut(stack_idx).ok_or(StackOverflow));
                *stack_top = val;
                stack_idx += 1;
            },

            Inst::Dup => {
                if stack_idx < 1 { return Err(StackUnderflow); }
                if stack_idx >= stack_size { return Err(StackOverflow); }
                unsafe {
                    *stack.get_unchecked_mut(stack_idx) = *stack.get_unchecked(stack_idx - 1);
                }
                stack_idx += 1;
            },

            Inst::Pop => {
                if stack_idx < 1 { return Err(StackUnderflow); }
                stack_idx -= 1;
            },

            Inst::Swap => {
                if stack_idx < 2 { return Err(StackUnderflow); }
                unsafe {
                    let tmp = *stack.get_unchecked(stack_idx - 1);
                    *stack.get_unchecked_mut(stack_idx - 1) = *stack.get_unchecked(stack_idx - 2);
                    *stack.get_unchecked_mut(stack_idx - 2) = tmp;
                }
            },

            Inst::Add => {
                if stack_idx < 2 { return Err(StackUnderflow); }
                unsafe {
                    *stack.get_unchecked_mut(stack_idx - 2) += *stack.get_unchecked(stack_idx - 1);
                }
                stack_idx -= 1;
            },

            Inst::Print => {
                let val = *try!(stack.get(stack_idx - 1).ok_or(StackUnderflow));
                println!("{}", val);
                stack_idx -= 1;
            },

            Inst::Halt => {
                return Ok(stack_idx);
            },
        }

        println!("{:?}", &stack[..stack_idx]);
    }

    Ok(stack_idx)
}

fn main() {
    let program = &[
        Inst::Push as u8, 1, 0, 0, 0,
        Inst::Push as u8, 2, 0, 0, 0,
        Inst::Add as u8,
        Inst::Print as u8,
    ];
    execute(program, &mut [0; 256], 0).unwrap();
}
<commit_msg>Add relative jump instruction.<commit_after>extern crate byteorder;
use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};

// Bytecode instruction opcodes. The values of these opcodes should never
// change, to remain compatible with existing bytecode programs.
//
// To make the from_u8 function work, MAX_INST_VARIANT must be kept in sync with
// this enum and every value from 0 to MAX_INST_VARIANT should be assigned to an
// opcode.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
enum Inst {
    Nop   = 0,
    Push  = 1,
    Dup   = 2,
    Pop   = 3,
    Swap  = 4,
    Add   = 5,
    Print = 6,
    Halt  = 7,
    Jump  = 8,
}
const MAX_INST_VARIANT: u8 = 8;

impl Inst {
    fn from_u8(x: u8) -> Option<Self> {
        if x <= MAX_INST_VARIANT {
            // This is safe as long as MAX_INST_VARIANT
            Some(unsafe { std::mem::transmute::<u8, Self>(x) })
        } else {
            None
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum VmError {
    InvalidOpcode,
    UnexpectedProgramEnd, // Hit end of program while reading opcode argument.
    StackOverflow,
    StackUnderflow,
}

fn execute(program: &[u8],
           stack: &mut [i32],
           mut stack_idx: usize) -> Result<usize, VmError> {
    use VmError::*;

    let stack_size = stack.len();
    let mut opcodes = Cursor::new(program);

    while let Ok(opcode) = opcodes.read_u8() {
        let inst = try!(Inst::from_u8(opcode).ok_or(InvalidOpcode));
        match inst {
            Inst::Nop => {},

            Inst::Push => {
                let val = try!(opcodes.read_i32::<LittleEndian>().or(Err(UnexpectedProgramEnd)));
                let stack_top = try!(stack.get_mut(stack_idx).ok_or(StackOverflow));
                *stack_top = val;
                stack_idx += 1;
            },

            Inst::Dup => {
                if stack_idx < 1 { return Err(StackUnderflow); }
                if stack_idx >= stack_size { return Err(StackOverflow); }
                unsafe {
                    *stack.get_unchecked_mut(stack_idx) = *stack.get_unchecked(stack_idx - 1);
                }
                stack_idx += 1;
            },

            Inst::Pop => {
                if stack_idx < 1 { return Err(StackUnderflow); }
                stack_idx -= 1;
            },

            Inst::Swap => {
                if stack_idx < 2 { return Err(StackUnderflow); }
                unsafe {
                    let tmp = *stack.get_unchecked(stack_idx - 1);
                    *stack.get_unchecked_mut(stack_idx - 1) = *stack.get_unchecked(stack_idx - 2);
                    *stack.get_unchecked_mut(stack_idx - 2) = tmp;
                }
            },

            Inst::Add => {
                if stack_idx < 2 { return Err(StackUnderflow); }
                unsafe {
                    *stack.get_unchecked_mut(stack_idx - 2) += *stack.get_unchecked(stack_idx - 1);
                }
                stack_idx -= 1;
            },

            Inst::Print => {
                let val = *try!(stack.get(stack_idx - 1).ok_or(StackUnderflow));
                println!("{}", val);
                stack_idx -= 1;
            },

            Inst::Halt => {
                return Ok(stack_idx);
            },

            Inst::Jump => {
                let delta = try!(opcodes.read_i8().or(Err(UnexpectedProgramEnd)));
                let addr = (opcodes.position() as i64 + delta as i64) as u64;
                opcodes.set_position(addr);
            },
        }

        //println!("{:?}", &stack[..stack_idx]);
    }

    Ok(stack_idx)
}

fn main() {
    let program = &[
        Inst::Push as u8, 1, 0, 0, 0,
        Inst::Push as u8, 2, 0, 0, 0,
        Inst::Add as u8,
        Inst::Dup as u8,
        Inst::Print as u8,
        Inst::Jump as u8, -10i8 as u8,
    ];
    execute(program, &mut [0; 256], 0).unwrap();
}
<|endoftext|>