github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/os/os.go (about)

     1  // Copyright 2017 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 os provides template functions for interacting with the operating
    15  // system.
    16  package os
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	_os "os"
    22  	"path/filepath"
    23  
    24  	"github.com/gohugoio/hugo/deps"
    25  	"github.com/spf13/afero"
    26  	"github.com/spf13/cast"
    27  )
    28  
    29  // New returns a new instance of the os-namespaced template functions.
    30  func New(d *deps.Deps) *Namespace {
    31  	return &Namespace{
    32  		readFileFs: afero.NewReadOnlyFs(afero.NewCopyOnWriteFs(d.PathSpec.BaseFs.Content.Fs, d.PathSpec.BaseFs.Work)),
    33  		workFs:     d.PathSpec.BaseFs.Work,
    34  		deps:       d,
    35  	}
    36  }
    37  
    38  // Namespace provides template functions for the "os" namespace.
    39  type Namespace struct {
    40  	readFileFs afero.Fs
    41  	workFs     afero.Fs
    42  	deps       *deps.Deps
    43  }
    44  
    45  // Getenv retrieves the value of the environment variable named by the key.
    46  // It returns the value, which will be empty if the variable is not present.
    47  func (ns *Namespace) Getenv(key interface{}) (string, error) {
    48  	skey, err := cast.ToStringE(key)
    49  	if err != nil {
    50  		return "", nil
    51  	}
    52  
    53  	if err = ns.deps.ExecHelper.Sec().CheckAllowedGetEnv(skey); err != nil {
    54  		return "", err
    55  	}
    56  
    57  	return _os.Getenv(skey), nil
    58  }
    59  
    60  // readFile reads the file named by filename in the given filesystem
    61  // and returns the contents as a string.
    62  func readFile(fs afero.Fs, filename string) (string, error) {
    63  	filename = filepath.Clean(filename)
    64  	if filename == "" || filename == "." || filename == string(_os.PathSeparator) {
    65  		return "", errors.New("invalid filename")
    66  	}
    67  
    68  	b, err := afero.ReadFile(fs, filename)
    69  	if err != nil {
    70  		return "", err
    71  	}
    72  
    73  	return string(b), nil
    74  }
    75  
    76  // ReadFile reads the file named by filename relative to the configured WorkingDir.
    77  // It returns the contents as a string.
    78  // There is an upper size limit set at 1 megabytes.
    79  func (ns *Namespace) ReadFile(i interface{}) (string, error) {
    80  	s, err := cast.ToStringE(i)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  
    85  	if ns.deps.PathSpec != nil {
    86  		s = ns.deps.PathSpec.RelPathify(s)
    87  	}
    88  
    89  	return readFile(ns.readFileFs, s)
    90  }
    91  
    92  // ReadDir lists the directory contents relative to the configured WorkingDir.
    93  func (ns *Namespace) ReadDir(i interface{}) ([]_os.FileInfo, error) {
    94  	path, err := cast.ToStringE(i)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	list, err := afero.ReadDir(ns.workFs, path)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("failed to read directory %q: %s", path, err)
   102  	}
   103  
   104  	return list, nil
   105  }
   106  
   107  // FileExists checks whether a file exists under the given path.
   108  func (ns *Namespace) FileExists(i interface{}) (bool, error) {
   109  	path, err := cast.ToStringE(i)
   110  	if err != nil {
   111  		return false, err
   112  	}
   113  
   114  	if path == "" {
   115  		return false, errors.New("fileExists needs a path to a file")
   116  	}
   117  
   118  	status, err := afero.Exists(ns.readFileFs, path)
   119  	if err != nil {
   120  		return false, err
   121  	}
   122  
   123  	return status, nil
   124  }
   125  
   126  // Stat returns the os.FileInfo structure describing file.
   127  func (ns *Namespace) Stat(i interface{}) (_os.FileInfo, error) {
   128  	path, err := cast.ToStringE(i)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	if path == "" {
   134  		return nil, errors.New("fileStat needs a path to a file")
   135  	}
   136  
   137  	r, err := ns.readFileFs.Stat(path)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return r, nil
   143  }