github.com/rezahousseini/hugo@v0.32.3/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, 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.Fs == nil { 83 panic("Must have a fs") 84 } 85 err := helpers.SymbolicWalk(f.Fs.Source, f.Base, walker) 86 87 if err != nil { 88 jww.ERROR.Println(err) 89 if err == helpers.ErrWalkRootTooShort { 90 panic("The root path is too short. If this is a test, make sure to init the content paths.") 91 } 92 } 93 94 } 95 96 func (f *Filesystem) shouldRead(filename string, fi os.FileInfo) (bool, error) { 97 if fi.Mode()&os.ModeSymlink == os.ModeSymlink { 98 link, err := filepath.EvalSymlinks(filename) 99 if err != nil { 100 jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", filename, err) 101 return false, nil 102 } 103 linkfi, err := f.Fs.Source.Stat(link) 104 if err != nil { 105 jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err) 106 return false, nil 107 } 108 109 if !linkfi.Mode().IsRegular() { 110 jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", filename) 111 } 112 return false, nil 113 } 114 115 ignore := f.SourceSpec.IgnoreFile(filename) 116 117 if fi.IsDir() { 118 if ignore { 119 return false, filepath.SkipDir 120 } 121 return false, nil 122 } 123 124 if ignore { 125 return false, nil 126 } 127 128 return true, nil 129 }