github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/util/path.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package util
     7  
     8  import (
     9  	"errors"
    10  	"net/url"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"strings"
    17  )
    18  
    19  // EnsureAbsolutePath ensure that a path is absolute, making it
    20  // relative to absoluteBase if necessary
    21  func EnsureAbsolutePath(path, absoluteBase string) string {
    22  	if filepath.IsAbs(path) {
    23  		return path
    24  	}
    25  	return filepath.Join(absoluteBase, path)
    26  }
    27  
    28  const notRegularFileMode os.FileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
    29  
    30  // GetDirectorySize returns the disk consumption for a given path
    31  func GetDirectorySize(path string) (int64, error) {
    32  	var size int64
    33  	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
    34  		if info != nil && (info.Mode()&notRegularFileMode) == 0 {
    35  			size += info.Size()
    36  		}
    37  		return err
    38  	})
    39  	return size, err
    40  }
    41  
    42  // IsDir returns true if given path is a directory,
    43  // or returns false when it's a file or does not exist.
    44  func IsDir(dir string) (bool, error) {
    45  	f, err := os.Stat(dir)
    46  	if err == nil {
    47  		return f.IsDir(), nil
    48  	}
    49  	if os.IsNotExist(err) {
    50  		return false, nil
    51  	}
    52  	return false, err
    53  }
    54  
    55  // IsFile returns true if given path is a file,
    56  // or returns false when it's a directory or does not exist.
    57  func IsFile(filePath string) (bool, error) {
    58  	f, err := os.Stat(filePath)
    59  	if err == nil {
    60  		return !f.IsDir(), nil
    61  	}
    62  	if os.IsNotExist(err) {
    63  		return false, nil
    64  	}
    65  	return false, err
    66  }
    67  
    68  // IsExist checks whether a file or directory exists.
    69  // It returns false when the file or directory does not exist.
    70  func IsExist(path string) (bool, error) {
    71  	_, err := os.Stat(path)
    72  	if err == nil || os.IsExist(err) {
    73  		return true, nil
    74  	}
    75  	if os.IsNotExist(err) {
    76  		return false, nil
    77  	}
    78  	return false, err
    79  }
    80  
    81  func statDir(dirPath, recPath string, includeDir, isDirOnly, followSymlinks bool) ([]string, error) {
    82  	dir, err := os.Open(dirPath)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	defer dir.Close()
    87  
    88  	fis, err := dir.Readdir(0)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	statList := make([]string, 0)
    94  	for _, fi := range fis {
    95  		if strings.Contains(fi.Name(), ".DS_Store") {
    96  			continue
    97  		}
    98  
    99  		relPath := path.Join(recPath, fi.Name())
   100  		curPath := path.Join(dirPath, fi.Name())
   101  		if fi.IsDir() {
   102  			if includeDir {
   103  				statList = append(statList, relPath+"/")
   104  			}
   105  			s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks)
   106  			if err != nil {
   107  				return nil, err
   108  			}
   109  			statList = append(statList, s...)
   110  		} else if !isDirOnly {
   111  			statList = append(statList, relPath)
   112  		} else if followSymlinks && fi.Mode()&os.ModeSymlink != 0 {
   113  			link, err := os.Readlink(curPath)
   114  			if err != nil {
   115  				return nil, err
   116  			}
   117  
   118  			isDir, err := IsDir(link)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  			if isDir {
   123  				if includeDir {
   124  					statList = append(statList, relPath+"/")
   125  				}
   126  				s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks)
   127  				if err != nil {
   128  					return nil, err
   129  				}
   130  				statList = append(statList, s...)
   131  			}
   132  		}
   133  	}
   134  	return statList, nil
   135  }
   136  
   137  // StatDir gathers information of given directory by depth-first.
   138  // It returns slice of file list and includes subdirectories if enabled;
   139  // it returns error and nil slice when error occurs in underlying functions,
   140  // or given path is not a directory or does not exist.
   141  //
   142  // Slice does not include given path itself.
   143  // If subdirectories is enabled, they will have suffix '/'.
   144  func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
   145  	if isDir, err := IsDir(rootPath); err != nil {
   146  		return nil, err
   147  	} else if !isDir {
   148  		return nil, errors.New("not a directory or does not exist: " + rootPath)
   149  	}
   150  
   151  	isIncludeDir := false
   152  	if len(includeDir) != 0 {
   153  		isIncludeDir = includeDir[0]
   154  	}
   155  	return statDir(rootPath, "", isIncludeDir, false, false)
   156  }
   157  
   158  func isOSWindows() bool {
   159  	return runtime.GOOS == "windows"
   160  }
   161  
   162  // FileURLToPath extracts the path information from a file://... url.
   163  func FileURLToPath(u *url.URL) (string, error) {
   164  	if u.Scheme != "file" {
   165  		return "", errors.New("URL scheme is not 'file': " + u.String())
   166  	}
   167  
   168  	path := u.Path
   169  
   170  	if !isOSWindows() {
   171  		return path, nil
   172  	}
   173  
   174  	// If it looks like there's a Windows drive letter at the beginning, strip off the leading slash.
   175  	re := regexp.MustCompile("/[A-Za-z]:/")
   176  	if re.MatchString(path) {
   177  		return path[1:], nil
   178  	}
   179  	return path, nil
   180  }
   181  
   182  // HomeDir returns path of '~'(in Linux) on Windows,
   183  // it returns error when the variable does not exist.
   184  func HomeDir() (home string, err error) {
   185  	// TODO: some users run GitBundle with mismatched uid  and "HOME=xxx" (they set HOME=xxx by environment manually)
   186  	// TODO: when running gitbundle as a sub command inside git, the HOME directory is not the user's home directory
   187  	// so at the moment we can not use `user.Current().HomeDir`
   188  	if isOSWindows() {
   189  		home = os.Getenv("USERPROFILE")
   190  		if home == "" {
   191  			home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
   192  		}
   193  	} else {
   194  		home = os.Getenv("HOME")
   195  	}
   196  
   197  	if home == "" {
   198  		return "", errors.New("cannot get home directory")
   199  	}
   200  
   201  	return home, nil
   202  }