github.com/IBM/fsgo@v0.0.0-20220920202152-e16fd2119d49/match.go (about)

     1  // Copyright 2022 IBM Inc. All rights reserved
     2  // Copyright © 2014 Steve Francia <spf@spf13.com>
     3  //
     4  // SPDX-License-Identifier: Apache2.0
     5  package fsgo
     6  
     7  import (
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  )
    12  
    13  // Glob returns the names of all files matching pattern or nil
    14  // if there is no matching file. The syntax of patterns is the same
    15  // as in Match. The pattern may describe hierarchical names such as
    16  // /usr/*/bin/ed (assuming the Separator is '/').
    17  //
    18  // Glob ignores file system errors such as I/O errors reading directories.
    19  // The only possible returned error is ErrBadPattern, when pattern
    20  // is malformed.
    21  //
    22  // This was adapted from (http://golang.org/pkg/path/filepath) and uses several
    23  // built-ins from that package.
    24  func Glob(fs Fs, pattern string) (matches []string, err error) {
    25  	if !hasMeta(pattern) {
    26  		// Lstat not supported by a ll filesystems.
    27  		if _, err = lstatIfPossible(fs, pattern); err != nil {
    28  			return nil, nil
    29  		}
    30  		return []string{pattern}, nil
    31  	}
    32  
    33  	dir, file := filepath.Split(pattern)
    34  	switch dir {
    35  	case "":
    36  		dir = "."
    37  	case string(filepath.Separator):
    38  	// nothing
    39  	default:
    40  		dir = dir[0 : len(dir)-1] // chop off trailing separator
    41  	}
    42  
    43  	if !hasMeta(dir) {
    44  		return glob(fs, dir, file, nil)
    45  	}
    46  
    47  	var m []string
    48  	m, err = Glob(fs, dir)
    49  	if err != nil {
    50  		return
    51  	}
    52  	for _, d := range m {
    53  		matches, err = glob(fs, d, file, matches)
    54  		if err != nil {
    55  			return
    56  		}
    57  	}
    58  	return
    59  }
    60  
    61  // glob searches for files matching pattern in the directory dir
    62  // and appends them to matches. If the directory cannot be
    63  // opened, it returns the existing matches. New matches are
    64  // added in lexicographical order.
    65  func glob(fs Fs, dir, pattern string, matches []string) (m []string, e error) {
    66  	m = matches
    67  	fi, err := fs.Stat(dir)
    68  	if err != nil {
    69  		return
    70  	}
    71  	if !fi.IsDir() {
    72  		return
    73  	}
    74  	d, err := fs.Open(dir)
    75  	if err != nil {
    76  		return
    77  	}
    78  	defer d.Close()
    79  
    80  	names, _ := d.Readdirnames(-1)
    81  	sort.Strings(names)
    82  
    83  	for _, n := range names {
    84  		matched, err := filepath.Match(pattern, n)
    85  		if err != nil {
    86  			return m, err
    87  		}
    88  		if matched {
    89  			m = append(m, filepath.Join(dir, n))
    90  		}
    91  	}
    92  	return
    93  }
    94  
    95  // hasMeta reports whether path contains any of the magic characters
    96  // recognized by Match.
    97  func hasMeta(path string) bool {
    98  	// TODO(niemeyer): Should other magic characters be added here?
    99  	return strings.ContainsAny(path, "*?[")
   100  }