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  }