// Glob returns the names of all files matching pattern or nil // if there is no matching file. The syntax of patterns is the same // as in path.Match. The pattern may describe hierarchical names such as // /usr/*/bin/ed. // // Glob ignores file system errors such as I/O errors reading directories. // The only possible returned error is ErrBadPattern, when pattern // is malformed. func Glob(fs vfs.FileSystem, pattern string) (matches []string, err error) { if !hasMeta(pattern) { if _, err = fs.Lstat(pattern); err != nil { return nil, nil } return []string{pattern}, nil } dir, file := path.Split(pattern) switch dir { case "": dir = "." case string(separator): // nothing default: dir = dir[0 : len(dir)-1] // chop off trailing separator } if !hasMeta(dir) { return glob(fs, dir, file, nil) } var m []string m, err = Glob(fs, dir) if err != nil { return } for _, d := range m { matches, err = glob(fs, d, file, matches) if err != nil { return } } return }
// glob searches for files matching pattern in the directory dir // and appends them to matches. If the directory cannot be // opened, it returns the existing matches. New matches are // added in lexicographical order. func glob(fs vfs.FileSystem, dir, pattern string, matches []string) (m []string, e error) { m = matches fi, err := fs.Stat(dir) if err != nil { return } if !fi.IsDir() { return } fis, err := fs.ReadDir(dir) if err != nil { return } sort.Sort(byName(fis)) for _, fi := range fis { n := fi.Name() matched, err := path.Match(path.Clean(pattern), n) if err != nil { return m, err } if matched { m = append(m, path.Join(dir, n)) } } return }
// Walk walks the filesystem rooted at root, calling walkFn for each file or // directory in the filesystem, including root. All errors that arise visiting files // and directories are filtered by walkFn. The files are walked in lexical // order. func Walk(fs vfs.FileSystem, root string, walkFn filepath.WalkFunc) error { info, err := fs.Lstat(root) if err != nil { return walkFn(root, nil, err) } return walk(fs, root, info, walkFn) }
//VFSStaticServer returns a StaticServer to serve a Godoc virtual file system //as defined in golang.org/x/tools/godoc/vfs . The Godoc vfs package contains //implementations for FileSystem, Map and Zip File based virtual file systems //Use errorHandlers to provide custom http.HandlerFunc to handle http.StatusNotFound //and http.StatusInternalServerError or provide nil to use default implementation //If a log.Logger is provided (ie. not nil), StaticServer does verbose logging func VFSStaticServer(f vfs.FileSystem, errorHandlers map[int]http.HandlerFunc, logger *log.Logger) StaticServer { return StaticServer{ stat: f.Lstat, readerfn: func(name string) (io.ReadSeeker, error) { rsc, err := f.Open(name) return io.ReadSeeker(rsc), err }, errorHandlers: setupErrorHandlers(errorHandlers), logger: logger, } }
// readDirNames reads the directory named by dirname and returns // a sorted list of directory entries. func readDirNames(fs vfs.FileSystem, dirname string) ([]string, error) { fis, err := fs.ReadDir(dirname) if err != nil { return nil, err } names := make([]string, len(fis)) for i := range fis { names[i] = fis[i].Name() } sort.Strings(names) return names, nil }
func readJSONFileFS(fs vfs.FileSystem, file string, v interface{}) (err error) { f, err := fs.Open(file) if err != nil { return err } defer func() { err2 := f.Close() if err == nil { err = err2 } }() return json.NewDecoder(f).Decode(v) }
// readDirNames reads the directory named by dirname and returns // a sorted list of directory entries. func readDirNames(fs vfs.FileSystem, dirname string) ([]string, error) { dirname = filepath.ToSlash(dirname) fileInfoList, err := fs.ReadDir(dirname) if err != nil { return nil, err } var names []string for _, fi := range fileInfoList { names = append(names, fi.Name()) } sort.Strings(names) return names, nil }
// ReadCached reads a Tree's configuration from all of its source unit // definition files (which may either be in a local VFS rooted at a // .srclib-cache/<COMMITID> dir, or a remote VFS). It does not read // the Srcfile; the Srcfile's directives are already accounted for in // the cached source unit definition files. // // bdfs should be a VFS obtained from a call to // (buildstore.RepoBuildStore).Commit. func ReadCached(bdfs vfs.FileSystem) (*Tree, error) { if _, err := bdfs.Lstat("."); os.IsNotExist(err) { return nil, fmt.Errorf("build cache dir does not exist (did you run `srclib config` to create it)?") } else if err != nil { return nil, err } // Collect all **/*.unit.json files. var unitFiles []string unitSuffix := buildstore.DataTypeSuffix(unit.SourceUnit{}) w := fs.WalkFS(".", rwvfs.Walkable(rwvfs.ReadOnly(bdfs))) for w.Step() { if err := w.Err(); err != nil { return nil, err } if path := w.Path(); strings.HasSuffix(path, unitSuffix) { unitFiles = append(unitFiles, path) } } // Parse units sort.Strings(unitFiles) units := make([]*unit.SourceUnit, len(unitFiles)) par := parallel.NewRun(runtime.GOMAXPROCS(0)) for i_, unitFile_ := range unitFiles { i, unitFile := i_, unitFile_ par.Acquire() go func() { defer par.Release() f, err := bdfs.Open(unitFile) if err != nil { par.Error(err) return } if err := json.NewDecoder(f).Decode(&units[i]); err != nil { f.Close() par.Error(err) return } if err := f.Close(); err != nil { par.Error(err) return } }() } if err := par.Wait(); err != nil { return nil, err } return &Tree{SourceUnits: units}, nil }
// GetFileWithOptions gets a file and observes the options specified // in opt. If fs implements FileGetter, fs.GetFileWithOptions is // called; otherwise the options are applied on the client side after // fetching the whole file. func GetFileWithOptions(fs vfs.FileSystem, path string, opt GetFileOptions) (*FileWithRange, error) { if fg, ok := fs.(FileGetter); ok { return fg.GetFileWithOptions(path, opt) } fi, err := fs.Lstat(path) if err != nil { return nil, err } e := newTreeEntry(fi) fwr := FileWithRange{TreeEntry: e} if fi.Mode().IsDir() { ee, err := readDir(fs, path, opt.RecurseSingleSubfolder, true) if err != nil { return nil, err } sort.Sort(TreeEntriesByTypeByName(ee)) e.Entries = ee } else if fi.Mode().IsRegular() { f, err := fs.Open(path) if err != nil { return nil, err } defer f.Close() contents, err := ioutil.ReadAll(f) if err != nil { return nil, err } e.Contents = contents if empty := (GetFileOptions{}); opt != empty { fr, _, err := ComputeFileRange(contents, opt) if err != nil { return nil, err } // Trim to only requested range. e.Contents = e.Contents[fr.StartByte:fr.EndByte] fwr.FileRange = *fr } } return &fwr, nil }
// readDir uses the passed vfs.FileSystem to read from starting at the base path. // If recurseSingleSubfolder is true, it will descend and include sub-folders // with a single sub-folder inside. first should always be set to true, other values are used internally. func readDir(fs vfs.FileSystem, base string, recurseSingleSubfolder bool, first bool) ([]*TreeEntry, error) { entries, err := fs.ReadDir(base) if err != nil { return nil, err } if recurseSingleSubfolder && !first && !singleSubDir(entries) { return nil, nil } te := make([]*TreeEntry, len(entries)) for i, fi := range entries { te[i] = newTreeEntry(fi) if fi.Mode().IsDir() && recurseSingleSubfolder { ee, err := readDir(fs, path.Join(base, fi.Name()), recurseSingleSubfolder, false) if err != nil { return nil, err } te[i].Entries = ee } } return te, nil }
func uploadFile(local vfs.FileSystem, remote rwvfs.FileSystem, path string, fi os.FileInfo, dryRun bool) error { kb := float64(fi.Size()) / 1024 if GlobalOpt.Verbose || dryRun { log.Printf("Uploading %s (%.1fkb)", path, kb) } if dryRun { return nil } lf, err := local.Open(path) if err != nil { return err } if err := rwvfs.MkdirAll(remote, filepath.Dir(path)); err != nil { return err } rf, err := remote.Create(path) if err != nil { return err } defer func() { if err := rf.Close(); err != nil { log.Println("Error closing after error:", err) } }() if _, err := io.Copy(rf, lf); err != nil { return err } if err := rf.Close(); err != nil { return err } if GlobalOpt.Verbose { log.Printf("Uploaded %s (%.1fkb)", path, kb) } return nil }
func fetchFile(remote vfs.FileSystem, local rwvfs.FileSystem, path string, fi os.FileInfo, dryRun bool) error { kb := float64(fi.Size()) / 1024 if GlobalOpt.Verbose || dryRun { log.Printf("Fetching %s (%.1fkb)", path, kb) } if dryRun { return nil } if err := rwvfs.MkdirAll(local, filepath.Dir(path)); err != nil { return err } rf, err := remote.Open(path) if err != nil { return fmt.Errorf("remote file: %s", err) } defer rf.Close() lf, err := local.Create(path) if err != nil { return fmt.Errorf("local file: %s", err) } defer lf.Close() if _, err := io.Copy(lf, rf); err != nil { return fmt.Errorf("copy from remote to local: %s", err) } if GlobalOpt.Verbose { log.Printf("Fetched %s (%.1fkb)", path, kb) } if err := lf.Close(); err != nil { return fmt.Errorf("local file: %s", err) } return nil }
// walk recursively descends path, calling walkFn. func walk(fs vfs.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { err := walkFn(path, info, nil) if err != nil { if info.IsDir() && err == filepath.SkipDir { return nil } return err } if !info.IsDir() { return nil } names, err := readDirNames(fs, path) if err != nil { return walkFn(path, info, err) } for _, name := range names { filename := pathpkg.Join(path, name) fileInfo, err := fs.Lstat(filename) if err != nil { if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { return err } } else { err = walk(fs, filename, fileInfo, walkFn) if err != nil { if !fileInfo.IsDir() || err != filepath.SkipDir { return err } } } } return nil }