github.com/SDLMoe/hugo@v0.47.1/source/filesystem.go (about) 1 // Copyright 2016 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package source 15 16 import ( 17 "os" 18 "path/filepath" 19 "runtime" 20 "sync" 21 22 "github.com/gohugoio/hugo/helpers" 23 jww "github.com/spf13/jwalterweatherman" 24 "golang.org/x/text/unicode/norm" 25 ) 26 27 type Filesystem struct { 28 files []ReadableFile 29 filesInit sync.Once 30 31 Base string 32 33 SourceSpec 34 } 35 36 type Input interface { 37 Files() []ReadableFile 38 } 39 40 func (sp SourceSpec) NewFilesystem(base string) *Filesystem { 41 return &Filesystem{SourceSpec: sp, Base: base} 42 } 43 44 func (f *Filesystem) Files() []ReadableFile { 45 f.filesInit.Do(func() { 46 f.captureFiles() 47 }) 48 return f.files 49 } 50 51 // add populates a file in the Filesystem.files 52 func (f *Filesystem) add(name string, fi os.FileInfo) (err error) { 53 var file ReadableFile 54 55 if runtime.GOOS == "darwin" { 56 // When a file system is HFS+, its filepath is in NFD form. 57 name = norm.NFC.String(name) 58 } 59 60 file = f.SourceSpec.NewFileInfo(f.Base, name, false, fi) 61 f.files = append(f.files, file) 62 63 return err 64 } 65 66 func (f *Filesystem) captureFiles() { 67 walker := func(filePath string, fi os.FileInfo, err error) error { 68 if err != nil { 69 return nil 70 } 71 72 b, err := f.shouldRead(filePath, fi) 73 if err != nil { 74 return err 75 } 76 if b { 77 f.add(filePath, fi) 78 } 79 return err 80 } 81 82 if f.SourceFs == nil { 83 panic("Must have a fs") 84 } 85 err := helpers.SymbolicWalk(f.SourceFs, f.Base, walker) 86 87 if err != nil { 88 jww.ERROR.Println(err) 89 } 90 91 } 92 93 func (f *Filesystem) shouldRead(filename string, fi os.FileInfo) (bool, error) { 94 if fi.Mode()&os.ModeSymlink == os.ModeSymlink { 95 link, err := filepath.EvalSymlinks(filename) 96 if err != nil { 97 jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", filename, err) 98 return false, nil 99 } 100 linkfi, err := f.SourceFs.Stat(link) 101 if err != nil { 102 jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err) 103 return false, nil 104 } 105 106 if !linkfi.Mode().IsRegular() { 107 jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", filename) 108 } 109 return false, nil 110 } 111 112 ignore := f.SourceSpec.IgnoreFile(filename) 113 114 if fi.IsDir() { 115 if ignore { 116 return false, filepath.SkipDir 117 } 118 return false, nil 119 } 120 121 if ignore { 122 return false, nil 123 } 124 125 return true, nil 126 }