(index<- )        ./libglob/lib.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Sat Apr 19 11:22:39 2014
   1  // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  /*!
  12   * Support for matching file paths against Unix shell style patterns.
  13   *
  14   * The `glob` and `glob_with` functions, in concert with the `Paths`
  15   * type, allow querying the filesystem for all files that match a particular
  16   * pattern - just like the libc `glob` function (for an example see the `glob`
  17   * documentation). The methods on the `Pattern` type provide functionality
  18   * for checking if individual paths match a particular pattern - in a similar
  19   * manner to the libc `fnmatch` function
  20   *
  21   * For consistency across platforms, and for Windows support, this module
  22   * is implemented entirely in Rust rather than deferring to the libc
  23   * `glob`/`fnmatch` functions.
  24   */
  25  
  26  #![crate_id = "glob#0.11-pre"]
  27  #![crate_type = "rlib"]
  28  #![crate_type = "dylib"]
  29  #![license = "MIT/ASL2"]
  30  #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
  31         html_favicon_url = "http://www.rust-lang.org/favicon.ico",
  32         html_root_url = "http://static.rust-lang.org/doc/master")]
  33  
  34  #![deny(deprecated_owned_vector)]
  35  
  36  use std::cell::Cell;
  37  use std::{cmp, os, path};
  38  use std::io::fs;
  39  use std::path::is_sep;
  40  use std::strbuf::StrBuf;
  41  
  42  /**
  43   * An iterator that yields Paths from the filesystem that match a particular
  44   * pattern - see the `glob` function for more details.
  45   */
  46  pub struct Paths {
  47      root: Path,
  48      dir_patterns: Vec<Pattern>,
  49      require_dir: bool,
  50      options: MatchOptions,
  51      todo: Vec<(Path,uint)>,
  52  }
  53  
  54  ///
  55  /// Return an iterator that produces all the Paths that match the given pattern,
  56  /// which may be absolute or relative to the current working directory.
  57  ///
  58  /// This method uses the default match options and is equivalent to calling
  59  /// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
  60  /// want to use non-default match options.
  61  ///
  62  /// # Example
  63  ///
  64  /// Consider a directory `/media/pictures` containing only the files `kittens.jpg`,
  65  /// `puppies.jpg` and `hamsters.gif`:
  66  ///
  67  /// ```rust
  68  /// use glob::glob;
  69  ///
  70  /// for path in glob("/media/pictures/*.jpg") {
  71  ///     println!("{}", path.display());
  72  /// }
  73  /// ```
  74  ///
  75  /// The above code will print:
  76  ///
  77  /// ```ignore
  78  /// /media/pictures/kittens.jpg
  79  /// /media/pictures/puppies.jpg
  80  /// ```
  81  ///
  82  pub fn glob(pattern: &str) -> Paths {
  83      glob_with(pattern, MatchOptions::new())
  84  }
  85  
  86  /**
  87   * Return an iterator that produces all the Paths that match the given pattern,
  88   * which may be absolute or relative to the current working directory.
  89   *
  90   * This function accepts Unix shell style patterns as described by `Pattern::new(..)`.
  91   * The options given are passed through unchanged to `Pattern::matches_with(..)` with
  92   * the exception that `require_literal_separator` is always set to `true` regardless of the
  93   * value passed to this function.
  94   *
  95   * Paths are yielded in alphabetical order, as absolute paths.
  96   */
  97  pub fn glob_with(pattern: &str, optionsMatchOptions) -> Paths {
  98      #[cfg(windows)]
  99      fn check_windows_verbatim(p: &Path) -> bool { path::windows::is_verbatim(p) }
 100      #[cfg(not(windows))]
 101      fn check_windows_verbatim(_&Path) -> bool { false }
 102  
 103      // calculate root this way to handle volume-relative Windows paths correctly
 104      let mut root = os::getcwd();
 105      let pat_root = Path::new(pattern).root_path();
 106      if pat_root.is_some() {
 107          if check_windows_verbatim(pat_root.get_ref()) {
 108              // FIXME: How do we want to handle verbatim paths? I'm inclined to return nothing,
 109              // since we can't very well find all UNC shares with a 1-letter server name.
 110              return Paths {
 111                  root: root,
 112                  dir_patterns: Vec::new(),
 113                  require_dir: false,
 114                  options: options,
 115                  todo: Vec::new(),
 116              };
 117          }
 118          root.push(pat_root.get_ref());
 119      }
 120  
 121      let root_len = pat_root.map_or(0u, |p| p.as_vec().len());
 122      let dir_patterns = pattern.slice_from(cmp::min(root_len, pattern.len()))
 123                         .split_terminator(is_sep)
 124                         .map(|s| Pattern::new(s))
 125                         .collect::<Vec<Pattern>>();
 126      let require_dir = pattern.chars().next_back().map(is_sep) == Some(true);
 127  
 128      let mut todo = Vec::new();
 129      if dir_patterns.len() > 0 {
 130          // Shouldn't happen, but we're using -1 as a special index.
 131          assert!(dir_patterns.len() < -1 as uint);
 132  
 133          fill_todo(&mut todo, dir_patterns.as_slice(), 0, &root, options);
 134      }
 135  
 136      Paths {
 137          root: root,
 138          dir_patterns: dir_patterns,
 139          require_dir: require_dir,
 140          options: options,
 141          todo: todo,
 142      }
 143  }
 144  
 145  impl Iterator<Path> for Paths {
 146  
 147      fn next(&mut self) -> Option<Path> {
 148          loop {
 149              if self.dir_patterns.is_empty() || self.todo.is_empty() {
 150                  return None;
 151              }
 152  
 153              let (path,idx) = self.todo.pop().unwrap();
 154              // idx -1: was already checked by fill_todo, maybe path was '.' or
 155              // '..' that we can't match here because of normalization.
 156              if idx == -1 as uint {
 157                  if self.require_dir && !path.is_dir() { continue; }
 158                  return Some(path);
 159              }
 160              let ref pattern = *self.dir_patterns.get(idx);
 161  
 162              if pattern.matches_with(match path.filename_str() {
 163                  // this ugly match needs to go here to avoid a borrowck error
 164                  None => {
 165                      // FIXME (#9639): How do we handle non-utf8 filenames? Ignore them for now
 166                      // Ideally we'd still match them against a *
 167                      continue;
 168                  }
 169                  Some(x) => x
 170              }, self.options) {
 171                  if idx == self.dir_patterns.len() - 1 {
 172                      // it is not possible for a pattern to match a directory *AND* its children
 173                      // so we don't need to check the children
 174  
 175                      if !self.require_dir || path.is_dir() {
 176                          return Some(path);
 177                      }
 178                  } else {
 179                      fill_todo(&mut self.todo, self.dir_patterns.as_slice(),
 180                                idx + 1, &path, self.options);
 181                  }
 182              }
 183          }
 184      }
 185  
 186  }
 187  
 188  fn list_dir_sorted(path: &Path) -> Option<Vec<Path>> {
 189      match fs::readdir(path) {
 190          Ok(mut children) => {
 191              children.sort_by(|p1, p2| p2.filename().cmp(&p1.filename()));
 192              Some(children.move_iter().collect())
 193          }
 194          Err(..) => None
 195      }
 196  }
 197  
 198  /**
 199   * A compiled Unix shell style pattern.
 200   */
 201  #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, Hash, Default)]
 202  pub struct Pattern {
 203      tokens: Vec<PatternToken>,
 204  }
 205  
 206  #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, Hash)]
 207  enum PatternToken {
 208      Char(char),
 209      AnyChar,
 210      AnySequence,
 211      AnyWithin(Vec<CharSpecifier> ),
 212      AnyExcept(Vec<CharSpecifier> )
 213  }
 214  
 215  #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, Hash)]
 216  enum CharSpecifier {
 217      SingleChar(char),
 218      CharRange(char, char)
 219  }
 220  
 221  #[deriving(Eq)]
 222  enum MatchResult {
 223      Match,
 224      SubPatternDoesntMatch,
 225      EntirePatternDoesntMatch
 226  }
 227  
 228  impl Pattern {
 229  
 230      /**
 231       * This function compiles Unix shell style patterns: `?` matches any single
 232       * character, `*` matches any (possibly empty) sequence of characters and
 233       * `[...]` matches any character inside the brackets, unless the first
 234       * character is `!` in which case it matches any character except those
 235       * between the `!` and the `]`. Character sequences can also specify ranges
 236       * of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any
 237       * character between 0 and 9 inclusive.
 238       *
 239       * The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
 240       * (e.g. `[?]`).  When a `]` occurs immediately following `[` or `[!` then
 241       * it is interpreted as being part of, rather then ending, the character
 242       * set, so `]` and NOT `]` can be matched by `[]]` and `[!]]` respectively.
 243       * The `-` character can be specified inside a character sequence pattern by
 244       * placing it at the start or the end, e.g. `[abc-]`.
 245       *
 246       * When a `[` does not have a closing `]` before the end of the string then
 247       * the `[` will be treated literally.
 248       */
 249      pub fn new(pattern&str) -> Pattern {
 250  
 251          let chars = pattern.chars().collect::<Vec<_>>();
 252          let mut tokens = Vec::new();
 253          let mut i = 0;
 254  
 255          while i < chars.len() {
 256              match *chars.get(i) {
 257                  '?' => {
 258                      tokens.push(AnyChar);
 259                      i += 1;
 260                  }
 261                  '*' => {
 262                      // *, **, ***, ****, ... are all equivalent
 263                      while i < chars.len() && *chars.get(i) == '*' {
 264                          i += 1;
 265                      }
 266                      tokens.push(AnySequence);
 267                  }
 268                  '[' => {
 269  
 270                      if i <= chars.len() - 4 && *chars.get(i + 1) == '!' {
 271                          match chars.slice_from(i + 3).position_elem(&']') {
 272                              None => (),
 273                              Some(j) => {
 274                                  let chars = chars.slice(i + 2, i + 3 + j);
 275                                  let cs = parse_char_specifiers(chars);
 276                                  tokens.push(AnyExcept(cs));
 277                                  i += j + 4;
 278                                  continue;
 279                              }
 280                          }
 281                      }
 282                      else if i <= chars.len() - 3 && *chars.get(i + 1) != '!' {
 283                          match chars.slice_from(i + 2).position_elem(&']') {
 284                              None => (),
 285                              Some(j) => {
 286                                  let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
 287                                  tokens.push(AnyWithin(cs));
 288                                  i += j + 3;
 289                                  continue;
 290                              }
 291                          }
 292                      }
 293  
 294                      // if we get here then this is not a valid range pattern
 295                      tokens.push(Char('['));
 296                      i += 1;
 297                  }
 298                  c => {
 299                      tokens.push(Char(c));
 300                      i += 1;
 301                  }
 302              }
 303          }
 304  
 305          Pattern { tokens: tokens }
 306      }
 307  
 308      /**
 309       * Escape metacharacters within the given string by surrounding them in
 310       * brackets. The resulting string will, when compiled into a `Pattern`,
 311       * match the input string and nothing else.
 312       */
 313      pub fn escape(s&str) -> ~str {
 314          let mut escaped = StrBuf::new();
 315          for c in s.chars() {
 316              match c {
 317                  // note that ! does not need escaping because it is only special inside brackets
 318                  '?' | '*' | '[' | ']' => {
 319                      escaped.push_char('[');
 320                      escaped.push_char(c);
 321                      escaped.push_char(']');
 322                  }
 323                  c => {
 324                      escaped.push_char(c);
 325                  }
 326              }
 327          }
 328          escaped.into_owned()
 329      }
 330  
 331      /**
 332       * Return if the given `str` matches this `Pattern` using the default
 333       * match options (i.e. `MatchOptions::new()`).
 334       *
 335       * # Example
 336       *
 337       * ```rust
 338       * use glob::Pattern;
 339       *
 340       * assert!(Pattern::new("c?t").matches("cat"));
 341       * assert!(Pattern::new("k[!e]tteh").matches("kitteh"));
 342       * assert!(Pattern::new("d*g").matches("doog"));
 343       * ```
 344       */
 345      pub fn matches(&self, str&str) -> bool {
 346          self.matches_with(str, MatchOptions::new())
 347      }
 348  
 349      /**
 350       * Return if the given `Path`, when converted to a `str`, matches this `Pattern`
 351       * using the default match options (i.e. `MatchOptions::new()`).
 352       */
 353      pub fn matches_path(&self, path&Path) -> bool {
 354          // FIXME (#9639): This needs to handle non-utf8 paths
 355          path.as_str().map_or(false, |s| {
 356              self.matches(s)
 357          })
 358      }
 359  
 360      /**
 361       * Return if the given `str` matches this `Pattern` using the specified match options.
 362       */
 363      pub fn matches_with(&self, str&str, optionsMatchOptions) -> bool {
 364          self.matches_from(None, str, 0, options) == Match
 365      }
 366  
 367      /**
 368       * Return if the given `Path`, when converted to a `str`, matches this `Pattern`
 369       * using the specified match options.
 370       */
 371      pub fn matches_path_with(&self, path&Path, optionsMatchOptions) -> bool {
 372          // FIXME (#9639): This needs to handle non-utf8 paths
 373          path.as_str().map_or(false, |s| {
 374              self.matches_with(s, options)
 375          })
 376      }
 377  
 378      fn matches_from(&self,
 379                      prev_charOption<char>,
 380                      mut file&str,
 381                      iuint,
 382                      optionsMatchOptions) -> MatchResult {
 383  
 384          let prev_char = Cell::new(prev_char);
 385  
 386          let require_literal = |c| {
 387              (options.require_literal_separator && is_sep(c)) ||
 388              (options.require_literal_leading_dot && c == '.'
 389               && is_sep(prev_char.get().unwrap_or('/')))
 390          };
 391  
 392          for (ti, token) in self.tokens.slice_from(i).iter().enumerate() {
 393              match *token {
 394                  AnySequence => {
 395                      loop {
 396                          match self.matches_from(prev_char.get(), file, i + ti + 1, options) {
 397                              SubPatternDoesntMatch => (), // keep trying
 398                              m => return m,
 399                          }
 400  
 401                          if file.is_empty() {
 402                              return EntirePatternDoesntMatch;
 403                          }
 404  
 405                          let (some_c, next) = file.slice_shift_char();
 406                          if require_literal(some_c.unwrap()) {
 407                              return SubPatternDoesntMatch;
 408                          }
 409                          prev_char.set(some_c);
 410                          file = next;
 411                      }
 412                  }
 413                  _ => {
 414                      if file.is_empty() {
 415                          return EntirePatternDoesntMatch;
 416                      }
 417  
 418                      let (some_c, next) = file.slice_shift_char();
 419                      let c = some_c.unwrap();
 420                      let matches = match *token {
 421                          AnyChar => {
 422                              !require_literal(c)
 423                          }
 424                          AnyWithin(ref specifiers) => {
 425                              !require_literal(c) &&
 426                                  in_char_specifiers(specifiers.as_slice(),
 427                                                     c,
 428                                                     options)
 429                          }
 430                          AnyExcept(ref specifiers) => {
 431                              !require_literal(c) &&
 432                                  !in_char_specifiers(specifiers.as_slice(),
 433                                                      c,
 434                                                      options)
 435                          }
 436                          Char(c2) => {
 437                              chars_eq(c, c2, options.case_sensitive)
 438                          }
 439                          AnySequence => {
 440                              unreachable!()
 441                          }
 442                      };
 443                      if !matches {
 444                          return SubPatternDoesntMatch;
 445                      }
 446                      prev_char.set(some_c);
 447                      file = next;
 448                  }
 449              }
 450          }
 451  
 452          if file.is_empty() {
 453              Match
 454          } else {
 455              SubPatternDoesntMatch
 456          }
 457      }
 458  
 459  }
 460  
 461  // Fills `todo` with paths under `path` to be matched by `patterns[idx]`,
 462  // special-casing patterns to match `.` and `..`, and avoiding `readdir()`
 463  // calls when there are no metacharacters in the pattern.
 464  fn fill_todo(todo: &mut Vec<(Path, uint)>, patterns: &[Pattern], idx: uint, path: &Path,
 465               optionsMatchOptions) {
 466      // convert a pattern that's just many Char(_) to a string
 467      fn pattern_as_str(pattern&Pattern) -> Option<StrBuf> {
 468          let mut s = StrBuf::new();
 469          for token in pattern.tokens.iter() {
 470              match *token {
 471                  Char(c) => s.push_char(c),
 472                  _ => return None
 473              }
 474          }
 475          return Some(s);
 476      }
 477  
 478      let add = |todo&mut Vec<_>, next_pathPath{
 479          if idx + 1 == patterns.len() {
 480              // We know it's good, so don't make the iterator match this path
 481              // against the pattern again. In particular, it can't match
 482              // . or .. globs since these never show up as path components.
 483              todo.push((next_path, -1 as uint));
 484          } else {
 485              fill_todo(todo, patterns, idx + 1, &next_path, options);
 486          }
 487      };
 488  
 489      let pattern = &patterns[idx];
 490  
 491      match pattern_as_str(pattern) {
 492          Some(s) => {
 493              // This pattern component doesn't have any metacharacters, so we
 494              // don't need to read the current directory to know where to
 495              // continue. So instead of passing control back to the iterator,
 496              // we can just check for that one entry and potentially recurse
 497              // right away.
 498              let special = "." == s.as_slice() || ".." == s.as_slice();
 499              let next_path = path.join(s.as_slice());
 500              if (special && path.is_dir()) || (!special && next_path.exists()) {
 501                  add(todo, next_path);
 502              }
 503          },
 504          None => {
 505              match list_dir_sorted(path) {
 506                  Some(entries) => {
 507                      todo.extend(entries.move_iter().map(|x|(x, idx)));
 508  
 509                      // Matching the special directory entries . and .. that refer to
 510                      // the current and parent directory respectively requires that
 511                      // the pattern has a leading dot, even if the `MatchOptions` field
 512                      // `require_literal_leading_dot` is not set.
 513                      if pattern.tokens.len() > 0 && pattern.tokens.get(0) == &Char('.') {
 514                          for &special in [".", ".."].iter() {
 515                              if pattern.matches_with(special, options) {
 516                                  add(todo, path.join(special));
 517                              }
 518                          }
 519                      }
 520                  }
 521                  None => {}
 522              }
 523          }
 524      }
 525  }
 526  
 527  fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
 528      let mut cs = Vec::new();
 529      let mut i = 0;
 530      while i < s.len() {
 531          if i + 3 <= s.len() && s[i + 1] == '-' {
 532              cs.push(CharRange(s[i], s[i + 2]));
 533              i += 3;
 534          } else {
 535              cs.push(SingleChar(s[i]));
 536              i += 1;
 537          }
 538      }
 539      cs
 540  }
 541  
 542  fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, optionsMatchOptions) -> bool {
 543  
 544      for &specifier in specifiers.iter() {
 545          match specifier {
 546              SingleChar(sc) => {
 547                  if chars_eq(c, sc, options.case_sensitive) {
 548                      return true;
 549                  }
 550              }
 551              CharRange(start, end) => {
 552  
 553                  // FIXME: work with non-ascii chars properly (issue #1347)
 554                  if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
 555  
 556                      let start = start.to_ascii().to_lower();
 557                      let end = end.to_ascii().to_lower();
 558  
 559                      let start_up = start.to_upper();
 560                      let end_up = end.to_upper();
 561  
 562                      // only allow case insensitive matching when
 563                      // both start and end are within a-z or A-Z
 564                      if start != start_up && end != end_up {
 565                          let start = start.to_char();
 566                          let end = end.to_char();
 567                          let c = c.to_ascii().to_lower().to_char();
 568                          if c >= start && c <= end {
 569                              return true;
 570                          }
 571                      }
 572                  }
 573  
 574                  if c >= start && c <= end {
 575                      return true;
 576                  }
 577              }
 578          }
 579      }
 580  
 581      false
 582  }
 583  
 584  /// A helper function to determine if two chars are (possibly case-insensitively) equal.
 585  fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
 586      if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
 587          true
 588      } else if !case_sensitive && a.is_ascii() && b.is_ascii() {
 589          // FIXME: work with non-ascii chars properly (issue #1347)
 590          a.to_ascii().eq_ignore_case(b.to_ascii())
 591      } else {
 592          a == b
 593      }
 594  }
 595  
 596  /**
 597   * Configuration options to modify the behaviour of `Pattern::matches_with(..)`
 598   */
 599  #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, Hash, Default)]
 600  pub struct MatchOptions {
 601  
 602      /**
 603       * Whether or not patterns should be matched in a case-sensitive manner. This
 604       * currently only considers upper/lower case relationships between ASCII characters,
 605       * but in future this might be extended to work with Unicode.
 606       */
 607      case_sensitive: bool,
 608  
 609      /**
 610       * If this is true then path-component separator characters (e.g. `/` on Posix)
 611       * must be matched by a literal `/`, rather than by `*` or `?` or `[...]`
 612       */
 613      require_literal_separator: bool,
 614  
 615      /**
 616       * If this is true then paths that contain components that start with a `.` will
 617       * not match unless the `.` appears literally in the pattern: `*`, `?` or `[...]`
 618       * will not match. This is useful because such files are conventionally considered
 619       * hidden on Unix systems and it might be desirable to skip them when listing files.
 620       */
 621      require_literal_leading_dot: bool
 622  }
 623  
 624  impl MatchOptions {
 625  
 626      /**
 627       * Constructs a new `MatchOptions` with default field values. This is used
 628       * when calling functions that do not take an explicit `MatchOptions` parameter.
 629       *
 630       * This function always returns this value:
 631       *
 632       * ```rust,ignore
 633       * MatchOptions {
 634       *     case_sensitive: true,
 635       *     require_literal_separator: false.
 636       *     require_literal_leading_dot: false
 637       * }
 638       * ```
 639       */
 640      pub fn new() -> MatchOptions {
 641          MatchOptions {
 642              case_sensitive: true,
 643              require_literal_separator: false,
 644              require_literal_leading_dot: false
 645          }
 646      }
 647  
 648  }
 649  
 650  #[cfg(test)]
 651  mod test {
 652      use std::os;
 653      use super::{glob, Pattern, MatchOptions};
 654  
 655      #[test]
 656      fn test_absolute_pattern() {
 657          // assume that the filesystem is not empty!
 658          assert!(glob("/*").next().is_some());
 659          assert!(glob("//").next().is_some());
 660  
 661          // check windows absolute paths with host/device components
 662          let root_with_device = os::getcwd().root_path().unwrap().join("*");
 663          // FIXME (#9639): This needs to handle non-utf8 paths
 664          assert!(glob(root_with_device.as_str().unwrap()).next().is_some());
 665      }
 666  
 667      #[test]
 668      fn test_wildcard_optimizations() {
 669          assert!(Pattern::new("a*b").matches("a___b"));
 670          assert!(Pattern::new("a**b").matches("a___b"));
 671          assert!(Pattern::new("a***b").matches("a___b"));
 672          assert!(Pattern::new("a*b*c").matches("abc"));
 673          assert!(!Pattern::new("a*b*c").matches("abcd"));
 674          assert!(Pattern::new("a*b*c").matches("a_b_c"));
 675          assert!(Pattern::new("a*b*c").matches("a___b___c"));
 676          assert!(Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabc"));
 677          assert!(!Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabca"));
 678          assert!(Pattern::new("a*a*a*a*a*a*a*a*a").matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
 679          assert!(Pattern::new("a*b[xyz]c*d").matches("abxcdbxcddd"));
 680      }
 681  
 682      #[test]
 683      fn test_lots_of_files() {
 684          // this is a good test because it touches lots of differently named files
 685          glob("/*/*/*/*").skip(10000).next();
 686      }
 687  
 688      #[test]
 689      fn test_range_pattern() {
 690  
 691          let pat = Pattern::new("a[0-9]b");
 692          for i in range(0, 10) {
 693              assert!(pat.matches(format!("a{}b", i)));
 694          }
 695          assert!(!pat.matches("a_b"));
 696  
 697          let pat = Pattern::new("a[!0-9]b");
 698          for i in range(0, 10) {
 699              assert!(!pat.matches(format!("a{}b", i)));
 700          }
 701          assert!(pat.matches("a_b"));
 702  
 703          let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
 704          for &p in pats.iter() {
 705              let pat = Pattern::new(p);
 706              for c in "abcdefghijklmnopqrstuvwxyz".chars() {
 707                  assert!(pat.matches(c.to_str()));
 708              }
 709              for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars() {
 710                  let options = MatchOptions {case_sensitive: false, .. MatchOptions::new()};
 711                  assert!(pat.matches_with(c.to_str(), options));
 712              }
 713              assert!(pat.matches("1"));
 714              assert!(pat.matches("2"));
 715              assert!(pat.matches("3"));
 716          }
 717  
 718          let pats = ["[abc-]", "[-abc]", "[a-c-]"];
 719          for &p in pats.iter() {
 720              let pat = Pattern::new(p);
 721              assert!(pat.matches("a"));
 722              assert!(pat.matches("b"));
 723              assert!(pat.matches("c"));
 724              assert!(pat.matches("-"));
 725              assert!(!pat.matches("d"));
 726          }
 727  
 728          let pat = Pattern::new("[2-1]");
 729          assert!(!pat.matches("1"));
 730          assert!(!pat.matches("2"));
 731  
 732          assert!(Pattern::new("[-]").matches("-"));
 733          assert!(!Pattern::new("[!-]").matches("-"));
 734      }
 735  
 736      #[test]
 737      fn test_unclosed_bracket() {
 738          // unclosed `[` should be treated literally
 739          assert!(Pattern::new("abc[def").matches("abc[def"));
 740          assert!(Pattern::new("abc[!def").matches("abc[!def"));
 741          assert!(Pattern::new("abc[").matches("abc["));
 742          assert!(Pattern::new("abc[!").matches("abc[!"));
 743          assert!(Pattern::new("abc[d").matches("abc[d"));
 744          assert!(Pattern::new("abc[!d").matches("abc[!d"));
 745          assert!(Pattern::new("abc[]").matches("abc[]"));
 746          assert!(Pattern::new("abc[!]").matches("abc[!]"));
 747      }
 748  
 749      #[test]
 750      fn test_pattern_matches() {
 751          let txt_pat = Pattern::new("*hello.txt");
 752          assert!(txt_pat.matches("hello.txt"));
 753          assert!(txt_pat.matches("gareth_says_hello.txt"));
 754          assert!(txt_pat.matches("some/path/to/hello.txt"));
 755          assert!(txt_pat.matches("some\\path\\to\\hello.txt"));
 756          assert!(txt_pat.matches("/an/absolute/path/to/hello.txt"));
 757          assert!(!txt_pat.matches("hello.txt-and-then-some"));
 758          assert!(!txt_pat.matches("goodbye.txt"));
 759  
 760          let dir_pat = Pattern::new("*some/path/to/hello.txt");
 761          assert!(dir_pat.matches("some/path/to/hello.txt"));
 762          assert!(dir_pat.matches("a/bigger/some/path/to/hello.txt"));
 763          assert!(!dir_pat.matches("some/path/to/hello.txt-and-then-some"));
 764          assert!(!dir_pat.matches("some/other/path/to/hello.txt"));
 765      }
 766  
 767      #[test]
 768      fn test_pattern_escape() {
 769          let s = "_[_]_?_*_!_";
 770          assert_eq!(Pattern::escape(s), "_[[]_[]]_[?]_[*]_!_".to_owned());
 771          assert!(Pattern::new(Pattern::escape(s)).matches(s));
 772      }
 773  
 774      #[test]
 775      fn test_pattern_matches_case_insensitive() {
 776  
 777          let pat = Pattern::new("aBcDeFg");
 778          let options = MatchOptions {
 779              case_sensitive: false,
 780              require_literal_separator: false,
 781              require_literal_leading_dot: false
 782          };
 783  
 784          assert!(pat.matches_with("aBcDeFg", options));
 785          assert!(pat.matches_with("abcdefg", options));
 786          assert!(pat.matches_with("ABCDEFG", options));
 787          assert!(pat.matches_with("AbCdEfG", options));
 788      }
 789  
 790      #[test]
 791      fn test_pattern_matches_case_insensitive_range() {
 792  
 793          let pat_within = Pattern::new("[a]");
 794          let pat_except = Pattern::new("[!a]");
 795  
 796          let options_case_insensitive = MatchOptions {
 797              case_sensitive: false,
 798              require_literal_separator: false,
 799              require_literal_leading_dot: false
 800          };
 801          let options_case_sensitive = MatchOptions {
 802              case_sensitive: true,
 803              require_literal_separator: false,
 804              require_literal_leading_dot: false
 805          };
 806  
 807          assert!(pat_within.matches_with("a", options_case_insensitive));
 808          assert!(pat_within.matches_with("A", options_case_insensitive));
 809          assert!(!pat_within.matches_with("A", options_case_sensitive));
 810  
 811          assert!(!pat_except.matches_with("a", options_case_insensitive));
 812          assert!(!pat_except.matches_with("A", options_case_insensitive));
 813          assert!(pat_except.matches_with("A", options_case_sensitive));
 814      }
 815  
 816      #[test]
 817      fn test_pattern_matches_require_literal_separator() {
 818  
 819          let options_require_literal = MatchOptions {
 820              case_sensitive: true,
 821              require_literal_separator: true,
 822              require_literal_leading_dot: false
 823          };
 824          let options_not_require_literal = MatchOptions {
 825              case_sensitive: true,
 826              require_literal_separator: false,
 827              require_literal_leading_dot: false
 828          };
 829  
 830          assert!(Pattern::new("abc/def").matches_with("abc/def", options_require_literal));
 831          assert!(!Pattern::new("abc?def").matches_with("abc/def", options_require_literal));
 832          assert!(!Pattern::new("abc*def").matches_with("abc/def", options_require_literal));
 833          assert!(!Pattern::new("abc[/]def").matches_with("abc/def", options_require_literal));
 834  
 835          assert!(Pattern::new("abc/def").matches_with("abc/def", options_not_require_literal));
 836          assert!(Pattern::new("abc?def").matches_with("abc/def", options_not_require_literal));
 837          assert!(Pattern::new("abc*def").matches_with("abc/def", options_not_require_literal));
 838          assert!(Pattern::new("abc[/]def").matches_with("abc/def", options_not_require_literal));
 839      }
 840  
 841      #[test]
 842      fn test_pattern_matches_require_literal_leading_dot() {
 843  
 844          let options_require_literal_leading_dot = MatchOptions {
 845              case_sensitive: true,
 846              require_literal_separator: false,
 847              require_literal_leading_dot: true
 848          };
 849          let options_not_require_literal_leading_dot = MatchOptions {
 850              case_sensitive: true,
 851              require_literal_separator: false,
 852              require_literal_leading_dot: false
 853          };
 854  
 855          let f = |options| Pattern::new("*.txt").matches_with(".hello.txt", options);
 856          assert!(f(options_not_require_literal_leading_dot));
 857          assert!(!f(options_require_literal_leading_dot));
 858  
 859          let f = |options| Pattern::new(".*.*").matches_with(".hello.txt", options);
 860          assert!(f(options_not_require_literal_leading_dot));
 861          assert!(f(options_require_literal_leading_dot));
 862  
 863          let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/.ccc", options);
 864          assert!(f(options_not_require_literal_leading_dot));
 865          assert!(!f(options_require_literal_leading_dot));
 866  
 867          let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/c.c.c.", options);
 868          assert!(f(options_not_require_literal_leading_dot));
 869          assert!(f(options_require_literal_leading_dot));
 870  
 871          let f = |options| Pattern::new("aaa/bbb/.*").matches_with("aaa/bbb/.ccc", options);
 872          assert!(f(options_not_require_literal_leading_dot));
 873          assert!(f(options_require_literal_leading_dot));
 874  
 875          let f = |options| Pattern::new("aaa/?bbb").matches_with("aaa/.bbb", options);
 876          assert!(f(options_not_require_literal_leading_dot));
 877          assert!(!f(options_require_literal_leading_dot));
 878  
 879          let f = |options| Pattern::new("aaa/[.]bbb").matches_with("aaa/.bbb", options);
 880          assert!(f(options_not_require_literal_leading_dot));
 881          assert!(!f(options_require_literal_leading_dot));
 882      }
 883  
 884      #[test]
 885      fn test_matches_path() {
 886          // on windows, (Path::new("a/b").as_str().unwrap() == "a\\b"), so this
 887          // tests that / and \ are considered equivalent on windows
 888          assert!(Pattern::new("a/b").matches_path(&Path::new("a/b")));
 889      }
 890  }


libglob/lib.rs:206:53-206:53 -enum- definition:
enum PatternToken {
    Char(char),
    AnyChar,
references:- 15
207: enum PatternToken {


libglob/lib.rs:201:62-201:62 -struct- definition:
pub struct Pattern {
    tokens: Vec<PatternToken>,
}
references:- 42


libglob/lib.rs:526:1-526:1 -fn- definition:
fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
    let mut cs = Vec::new();
    let mut i = 0;
references:- 2
285:                             Some(j) => {
286:                                 let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
287:                                 tokens.push(AnyWithin(cs));


libglob/lib.rs:45:4-45:4 -struct- definition:
 */
pub struct Paths {
    root: Path,
references:- 5
109:             // since we can't very well find all UNC shares with a 1-letter server name.
110:             return Paths {
111:                 root: root,
--
145: impl Iterator<Path> for Paths {


libglob/lib.rs:584:89-584:89 -fn- definition:
/// A helper function to determine if two chars are (possibly case-insensitively) equal.
fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
    if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
references:- 2
546:             SingleChar(sc) => {
547:                 if chars_eq(c, sc, options.case_sensitive) {
548:                     return true;


libglob/lib.rs:599:62-599:62 -struct- definition:
pub struct MatchOptions {
    /**
     * Whether or not patterns should be matched in a case-sensitive manner. This
references:- 45


libglob/lib.rs:463:58-463:58 -fn- definition:
// calls when there are no metacharacters in the pattern.
fn fill_todo(todo: &mut Vec<(Path, uint)>, patterns: &[Pattern], idx: uint, path: &Path,
             options: MatchOptions) {
references:- 3
178:                 } else {
179:                     fill_todo(&mut self.todo, self.dir_patterns.as_slice(),
180:                               idx + 1, &path, self.options);
--
484:         } else {
485:             fill_todo(todo, patterns, idx + 1, &next_path, options);
486:         }


libglob/lib.rs:221:16-221:16 -enum- definition:
enum MatchResult {
    Match,
    SubPatternDoesntMatch,
references:- 4
222: enum MatchResult {
--
381:                     i: uint,
382:                     options: MatchOptions) -> MatchResult {


libglob/lib.rs:215:53-215:53 -enum- definition:
enum CharSpecifier {
    SingleChar(char),
    CharRange(char, char)
references:- 18
527: fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
528:     let mut cs = Vec::new();
--
542: fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {


libglob/lib.rs:541:1-541:1 -fn- definition:
fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
    for &specifier in specifiers.iter() {
        match specifier {
references:- 2
431:                             !require_literal(c) &&
432:                                 !in_char_specifiers(specifiers.as_slice(),
433:                                                     c,