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

//! Support for matching file paths against Unix shell style patterns.
//!
//! The `glob` and `glob_with` functions, in concert with the `Paths`
//! type, allow querying the filesystem for all files that match a particular
//! pattern - just like the libc `glob` function (for an example see the `glob`
//! documentation). The methods on the `Pattern` type provide functionality
//! for checking if individual paths match a particular pattern - in a similar
//! manner to the libc `fnmatch` function
//! For consistency across platforms, and for Windows support, this module
//! is implemented entirely in Rust rather than deferring to the libc
//! `glob`/`fnmatch` functions.

#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/glob/")]
#![cfg_attr(test, feature(io))]
#![cfg_attr(all(test, windows), feature(std_misc))]
#![feature(path_components_peek, unmarked_api)]

use std::ascii::AsciiExt;
use std::cell::Cell;
use std::cmp;
use std::fmt;
use std::fs;
use std::io::prelude::*;
use std::io;
use std::path::{self, Path, PathBuf, Component};

use PatternToken::{Char, AnyChar, AnySequence, AnyRecursiveSequence, AnyWithin};
use PatternToken::AnyExcept;
use CharSpecifier::{SingleChar, CharRange};
use MatchResult::{Match, SubPatternDoesntMatch, EntirePatternDoesntMatch};

/// An iterator that yields `Path`s from the filesystem that match a particular
/// pattern.
///
/// Note that it yields `GlobResult` in order to report any `IoErrors` that may
/// arise during iteration. If a directory matches but is unreadable,
/// thereby preventing its contents from being checked for matches, a
/// `GlobError` is returned to express this.
///
/// See the `glob` function for more details.
pub struct Paths {
    dir_patterns: Vec<Pattern>,
    require_dir: bool,
    options: MatchOptions,
    todo: Vec<Result<(PathBuf, usize), GlobError>>,
    scope: Option<PathBuf>,
}

/// Return an iterator that produces all the Paths that match the given pattern,
/// which may be absolute or relative to the current working directory.
///
/// This may return an error if the pattern is invalid.
///
/// This method uses the default match options and is equivalent to calling
/// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
/// want to use non-default match options.
///
/// When iterating, each result is a `GlobResult` which expresses the
/// possibility that there was an `IoError` when attempting to read the contents
/// of the matched path.  In other words, each item returned by the iterator
/// will either be an `Ok(Path)` if the path matched, or an `Err(GlobError)` if
/// the path (partially) matched _but_ its contents could not be read in order
/// to determine if its contents matched.
///
/// See the `Paths` documentation for more information.
///
/// # Example
///
/// Consider a directory `/media/pictures` containing only the files
/// `kittens.jpg`, `puppies.jpg` and `hamsters.gif`:
///
/// ```rust
/// use glob::glob;
///
/// for entry in glob("/media/pictures/*.jpg").unwrap() {
///     match entry {
///         Ok(path) => println!("{:?}", path.display()),
///
///         // if the path matched but was unreadable,
///         // thereby preventing its contents from matching
///         Err(e) => println!("{:?}", e),
///     }
/// }
/// ```
///
/// The above code will print:
///
/// ```ignore
/// /media/pictures/kittens.jpg
/// /media/pictures/puppies.jpg
/// ```
///
/// If you want to ignore unreadable paths, you can use something like
/// `filter_map`:
///
/// ```rust
/// use glob::glob;
/// use std::result::Result;
///
/// for path in glob("/media/pictures/*.jpg").unwrap().filter_map(Result::ok) {
///     println!("{}", path.display());
/// }
/// ```
///
pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
    glob_with(pattern, &MatchOptions::new())
}

/// Return an iterator that produces all the Paths that match the given pattern,
/// which may be absolute or relative to the current working directory.
///
/// This may return an error if the pattern is invalid.
///
/// This function accepts Unix shell style patterns as described by
/// `Pattern::new(..)`.  The options given are passed through unchanged to
/// `Pattern::matches_with(..)` with the exception that
/// `require_literal_separator` is always set to `true` regardless of the value
/// passed to this function.
///
/// Paths are yielded in alphabetical order.
pub fn glob_with(pattern: &str, options: &MatchOptions)
                 -> Result<Paths, PatternError> {
    // make sure that the pattern is valid first, else early return with error
    let _compiled = try!(Pattern::new(pattern));

    #[cfg(windows)]
    fn check_windows_verbatim(p: &Path) -> bool {
        use std::path::Prefix;
        match p.components().next() {
            Some(Component::Prefix { parsed: ref p, .. }) => p.is_verbatim(),
            _ => false
        }
    }
    #[cfg(not(windows))]
    fn check_windows_verbatim(_: &Path) -> bool { false }

    #[cfg(windows)]
    fn to_scope(p: &Path) -> PathBuf {
        // FIXME handle volume relative paths here
        p.to_path_buf()
    }
    #[cfg(not(windows))]
    fn to_scope(p: &Path) -> PathBuf { p.to_path_buf() }

    let mut components = Path::new(pattern).components();
    loop {
        match components.peek() {
            Some(Component::Prefix(..)) |
            Some(Component::RootDir) => { components.next(); }
            _ => break,
        }
    }
    let root_len = pattern.len() - components.as_path().to_str().unwrap().len();
    let root = if root_len > 0 {Some(Path::new(&pattern[..root_len]))}
               else {None};

    if root_len > 0 && check_windows_verbatim(root.unwrap()) {
        // FIXME: How do we want to handle verbatim paths? I'm inclined to
        // return nothing, since we can't very well find all UNC shares with a
        // 1-letter server name.
        return Ok(Paths {
            dir_patterns: Vec::new(),
            require_dir: false,
            options: options.clone(),
            todo: Vec::new(),
            scope: None,
        });
    }

    let scope = root.map(to_scope).unwrap_or_else(|| PathBuf::new("."));

    let mut dir_patterns = Vec::new();
    let components = pattern[cmp::min(root_len, pattern.len())..]
                            .split_terminator(path::is_separator);

    for component in components {
        let compiled = try!(Pattern::new(component));
        dir_patterns.push(compiled);
    }

    let require_dir = pattern.chars().next_back().map(path::is_separator) == Some(true);
    let todo = Vec::new();

    Ok(Paths {
        dir_patterns: dir_patterns,
        require_dir: require_dir,
        options: options.clone(),
        todo: todo,
        scope: Some(scope),
    })
}

/// A glob iteration error.
///
/// This is typically returned when a particular path cannot be read
/// to determine if its contents match the glob pattern. This is possible
/// if the program lacks the permissions, for example.
#[derive(Debug)]
pub struct GlobError {<commit_msg>Fix failing test<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for matching file paths against Unix shell style patterns.
//!
//! The `glob` and `glob_with` functions, in concert with the `Paths`
//! type, allow querying the filesystem for all files that match a particular
//! pattern - just like the libc `glob` function (for an example see the `glob`
//! documentation). The methods on the `Pattern` type provide functionality
//! for checking if individual paths match a particular pattern - in a similar
//! manner to the libc `fnmatch` function
//! For consistency across platforms, and for Windows support, this module
//! is implemented entirely in Rust rather than deferring to the libc
//! `glob`/`fnmatch` functions.

#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/glob/")]
#![cfg_attr(test, feature(io))]
#![cfg_attr(all(test, windows), feature(std_misc))]

use std::ascii::AsciiExt;
use std::cell::Cell;
use std::cmp;
use std::fmt;
use std::fs;
use std::io::prelude::*;
use std::io;
use std::path::{self, Path, PathBuf, Component};

use PatternToken::{Char, AnyChar, AnySequence, AnyRecursiveSequence, AnyWithin};
use PatternToken::AnyExcept;
use CharSpecifier::{SingleChar, CharRange};
use MatchResult::{Match, SubPatternDoesntMatch, EntirePatternDoesntMatch};

/// An iterator that yields `Path`s from the filesystem that match a particular
/// pattern.
///
/// Note that it yields `GlobResult` in order to report any `IoErrors` that may
/// arise during iteration. If a directory matches but is unreadable,
/// thereby preventing its contents from being checked for matches, a
/// `GlobError` is returned to express this.
///
/// See the `glob` function for more details.
pub struct Paths {
    dir_patterns: Vec<Pattern>,
    require_dir: bool,
    options: MatchOptions,
    todo: Vec<Result<(PathBuf, usize), GlobError>>,
    scope: Option<PathBuf>,
}

/// Return an iterator that produces all the Paths that match the given pattern,
/// which may be absolute or relative to the current working directory.
///
/// This may return an error if the pattern is invalid.
///
/// This method uses the default match options and is equivalent to calling
/// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
/// want to use non-default match options.
///
/// When iterating, each result is a `GlobResult` which expresses the
/// possibility that there was an `IoError` when attempting to read the contents
/// of the matched path.  In other words, each item returned by the iterator
/// will either be an `Ok(Path)` if the path matched, or an `Err(GlobError)` if
/// the path (partially) matched _but_ its contents could not be read in order
/// to determine if its contents matched.
///
/// See the `Paths` documentation for more information.
///
/// # Example
///
/// Consider a directory `/media/pictures` containing only the files
/// `kittens.jpg`, `puppies.jpg` and `hamsters.gif`:
///
/// ```rust
/// use glob::glob;
///
/// for entry in glob("/media/pictures/*.jpg").unwrap() {
///     match entry {
///         Ok(path) => println!("{:?}", path.display()),
///
///         // if the path matched but was unreadable,
///         // thereby preventing its contents from matching
///         Err(e) => println!("{:?}", e),
///     }
/// }
/// ```
///
/// The above code will print:
///
/// ```ignore
/// /media/pictures/kittens.jpg
/// /media/pictures/puppies.jpg
/// ```
///
/// If you want to ignore unreadable paths, you can use something like
/// `filter_map`:
///
/// ```rust
/// use glob::glob;
/// use std::result::Result;
///
/// for path in glob("/media/pictures/*.jpg").unwrap().filter_map(Result::ok) {
///     println!("{}", path.display());
/// }
/// ```
///
pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
    glob_with(pattern, &MatchOptions::new())
}

/// Return an iterator that produces all the Paths that match the given pattern,
/// which may be absolute or relative to the current working directory.
///
/// This may return an error if the pattern is invalid.
///
/// This function accepts Unix shell style patterns as described by
/// `Pattern::new(..)`.  The options given are passed through unchanged to
/// `Pattern::matches_with(..)` with the exception that
/// `require_literal_separator` is always set to `true` regardless of the value
/// passed to this function.
///
/// Paths are yielded in alphabetical order.
pub fn glob_with(pattern: &str, options: &MatchOptions)
                 -> Result<Paths, PatternError> {
    // make sure that the pattern is valid first, else early return with error
    let _compiled = try!(Pattern::new(pattern));

    #[cfg(windows)]
    fn check_windows_verbatim(p: &Path) -> bool {
        use std::path::Prefix;
        match p.components().next() {
            Some(Component::Prefix { parsed: ref p, .. }) => p.is_verbatim(),
            _ => false
        }
    }
    #[cfg(not(windows))]
    fn check_windows_verbatim(_: &Path) -> bool { false }

    #[cfg(windows)]
    fn to_scope(p: &Path) -> PathBuf {
        // FIXME handle volume relative paths here
        p.to_path_buf()
    }
    #[cfg(not(windows))]
    fn to_scope(p: &Path) -> PathBuf { p.to_path_buf() }

    let mut components = Path::new(pattern).components().peekable();
    loop {
        match components.peek() {
            Some(&Component::Prefix(..)) |
            Some(&Component::RootDir) => { components.next(); }
            _ => break,
        }
    }
    let rest = components.map(|s| s.as_os_str()).collect::<PathBuf>();
    let normalized_pattern = Path::new(pattern).iter().collect::<PathBuf>();
    let root_len = normalized_pattern.to_str().unwrap().len() -
                   rest.to_str().unwrap().len();
    let root = if root_len > 0 {Some(Path::new(&pattern[..root_len]))}
               else {None};

    if root_len > 0 && check_windows_verbatim(root.unwrap()) {
        // FIXME: How do we want to handle verbatim paths? I'm inclined to
        // return nothing, since we can't very well find all UNC shares with a
        // 1-letter server name.
        return Ok(Paths {
            dir_patterns: Vec::new(),
            require_dir: false,
            options: options.clone(),
            todo: Vec::new(),
            scope: None,
        });
    }

    let scope = root.map(to_scope).unwrap_or_else(|| PathBuf::new("."));

    let mut dir_patterns = Vec::new();
    let components = pattern[cmp::min(root_len, pattern.len())..]
                            .split_terminator(path::is_separator);

    for component in components {
        let compiled = try!(Pattern::new(component));
        dir_patterns.push(compiled);
    }

    if root_len == pattern.len() {
        dir_patterns.push(Pattern {
            original: "".to_string(),
            tokens: Vec::new(),
            is_recursive: false,
        });
    }

    let require_dir = pattern.chars().next_back().map(path::is_separator) == Some(true);
    let todo = Vec::new();

    Ok(Paths {
        dir_patterns: dir_patterns,
        require_dir: require_dir,
        options: options.clone(),
        todo: todo,
        scope: Some(scope),
    })
}

/// A glob iteration error.
///
/// This is typically returned when a particular path cannot be read
/// to determine if its contents match the glob pattern. This is possible
/// if the program lacks the permissions, for example.
#[derive(Debug)]
pub struct GlobError {
{'fraction_non_alphanumeric': 0.14785992217898833, 'fraction_numerical': 0.0022963577215028385, 'mean_word_length': 2.8932207598708715, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2787152', 'n_tokens_mistral': 4594, 'n_tokens_neox': 4496, 'n_words': 1561}