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  }