github.com/pquerna/agent@v2.1.8+incompatible/glob/glob.go (about)

     1  // Source: https://github.com/aashah/glob
     2  
     3  package glob
     4  
     5  import (
     6  	"errors"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  )
    12  
    13  /*
    14   * glob - an expanded version
    15   *
    16   * This implementation of globbing will still take advantage of the Glob
    17   * function in path/filepath, however this extends the pattern to include '**'
    18   *
    19   */
    20  
    21  /*
    22  Algorithm details
    23  
    24  segments = glob pattern split by os.path separator
    25  define Entry:
    26      path, index into glob
    27  
    28  Base Case:
    29      add Entry{root, 0}
    30  
    31  while num entries > 0
    32      given an entry (path, idx)
    33      given glob segment (gb) at idx
    34  
    35      if gb == **
    36          move cur entry idx + 1
    37  
    38          for each dir inside path
    39              add new Entry{dir, idx}
    40      else
    41          add gb to path
    42          check for any results from normal globbing
    43          if none
    44              remove entry
    45          else
    46              if idx + 1 is out of bounds
    47                  add result to final list
    48              else
    49                  add an entry{result, idx + 1}
    50  
    51      keep current entry if it's idx is in bounds
    52  
    53  */
    54  
    55  type matchEntry struct {
    56  	path string
    57  	idx  int
    58  }
    59  
    60  func Glob(root string, pattern string) (matches []string, e error) {
    61  	if strings.Index(pattern, "**") < 0 {
    62  		return filepath.Glob(filepath.Join(root, pattern))
    63  	}
    64  
    65  	segments := strings.Split(pattern, string(os.PathSeparator))
    66  
    67  	workingEntries := []matchEntry{
    68  		matchEntry{path: root, idx: 0},
    69  	}
    70  
    71  	for len(workingEntries) > 0 {
    72  
    73  		var temp []matchEntry
    74  		for _, entry := range workingEntries {
    75  			workingPath := entry.path
    76  			idx := entry.idx
    77  			segment := segments[entry.idx]
    78  
    79  			if segment == "**" {
    80  				// add all subdirectories and move yourself one step further
    81  				// into pattern
    82  				entry.idx++
    83  
    84  				subDirectories, err := getAllSubDirectories(entry.path)
    85  
    86  				if err != nil {
    87  					return nil, err
    88  				}
    89  
    90  				for _, name := range subDirectories {
    91  					path := filepath.Join(workingPath, name)
    92  
    93  					newEntry := matchEntry{
    94  						path: path,
    95  						idx:  idx,
    96  					}
    97  
    98  					temp = append(temp, newEntry)
    99  				}
   100  
   101  			} else {
   102  				// look at all results
   103  				// if we're at the end of the pattern, we found a match
   104  				// else add it to a working entry
   105  				path := filepath.Join(workingPath, segment)
   106  				results, err := filepath.Glob(path)
   107  
   108  				if err != nil {
   109  					return nil, err
   110  				}
   111  
   112  				for _, result := range results {
   113  					if idx+1 < len(segments) {
   114  						newEntry := matchEntry{
   115  							path: result,
   116  							idx:  idx + 1,
   117  						}
   118  
   119  						temp = append(temp, newEntry)
   120  					} else {
   121  						matches = append(matches, result)
   122  					}
   123  				}
   124  				// delete ourself regardless
   125  				entry.idx = len(segments)
   126  			}
   127  
   128  			// check whether current entry is still valid
   129  			if entry.idx < len(segments) {
   130  				temp = append(temp, entry)
   131  			}
   132  		}
   133  
   134  		workingEntries = temp
   135  	}
   136  
   137  	return
   138  }
   139  
   140  // Calcualtes the glob root for a path.
   141  func Root(path string) string {
   142  	if filepath.IsAbs(path) {
   143  		if runtime.GOOS == "windows" {
   144  			return filepath.VolumeName(path)
   145  		} else {
   146  			return "/"
   147  		}
   148  	} else {
   149  		dir, _ := os.Getwd()
   150  		return dir
   151  	}
   152  }
   153  
   154  // Returns whether or not the file is a directory
   155  func IsDir(path string) (val bool, err error) {
   156  	fi, err := os.Stat(path)
   157  	if err != nil {
   158  		return false, err
   159  	}
   160  
   161  	return fi.IsDir(), nil
   162  }
   163  
   164  func getAllSubDirectories(path string) (dirs []string, err error) {
   165  
   166  	if dir, err := IsDir(path); err != nil || !dir {
   167  		return nil, errors.New("Not a directory " + path)
   168  	}
   169  
   170  	d, err := os.Open(path)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	files, err := d.Readdirnames(-1)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	for _, file := range files {
   181  		path := filepath.Join(path, file)
   182  		if dir, err := IsDir(path); err == nil && dir {
   183  			dirs = append(dirs, file)
   184  		}
   185  	}
   186  	return
   187  }