2017-10-27 09:10:54 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2017-10-27 09:10:54 +03:00
package util
2019-11-11 00:33:47 +03:00
import (
2020-12-22 02:40:57 +03:00
"errors"
2021-04-09 01:25:57 +03:00
"net/url"
2019-11-11 00:33:47 +03:00
"os"
2020-12-22 02:40:57 +03:00
"path"
2019-11-11 00:33:47 +03:00
"path/filepath"
2021-04-09 01:25:57 +03:00
"regexp"
"runtime"
2019-11-11 00:33:47 +03:00
)
2017-10-27 09:10:54 +03:00
// EnsureAbsolutePath ensure that a path is absolute, making it
// relative to absoluteBase if necessary
2021-12-20 07:41:31 +03:00
func EnsureAbsolutePath ( path , absoluteBase string ) string {
2017-10-27 09:10:54 +03:00
if filepath . IsAbs ( path ) {
return path
}
return filepath . Join ( absoluteBase , path )
}
2019-11-11 00:33:47 +03:00
2022-01-04 02:24:12 +03:00
const notRegularFileMode os . FileMode = os . ModeSymlink | os . ModeNamedPipe | os . ModeSocket | os . ModeDevice | os . ModeCharDevice | os . ModeIrregular
2019-11-11 00:33:47 +03:00
2022-01-04 02:24:12 +03:00
// GetDirectorySize returns the disk consumption for a given path
2019-11-11 00:33:47 +03:00
func GetDirectorySize ( path string ) ( int64 , error ) {
var size int64
err := filepath . Walk ( path , func ( _ string , info os . FileInfo , err error ) error {
if info != nil && ( info . Mode ( ) & notRegularFileMode ) == 0 {
size += info . Size ( )
}
return err
} )
return size , err
}
2020-11-28 05:42:08 +03:00
// IsDir returns true if given path is a directory,
// or returns false when it's a file or does not exist.
func IsDir ( dir string ) ( bool , error ) {
f , err := os . Stat ( dir )
if err == nil {
return f . IsDir ( ) , nil
}
if os . IsNotExist ( err ) {
return false , nil
}
return false , err
}
// IsFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func IsFile ( filePath string ) ( bool , error ) {
f , err := os . Stat ( filePath )
if err == nil {
return ! f . IsDir ( ) , nil
}
if os . IsNotExist ( err ) {
return false , nil
}
return false , err
}
// IsExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func IsExist ( path string ) ( bool , error ) {
_ , err := os . Stat ( path )
if err == nil || os . IsExist ( err ) {
return true , nil
}
if os . IsNotExist ( err ) {
return false , nil
}
return false , err
}
2020-12-22 02:40:57 +03:00
func statDir ( dirPath , recPath string , includeDir , isDirOnly , followSymlinks bool ) ( [ ] string , error ) {
dir , err := os . Open ( dirPath )
if err != nil {
return nil , err
}
defer dir . Close ( )
fis , err := dir . Readdir ( 0 )
if err != nil {
return nil , err
}
statList := make ( [ ] string , 0 )
for _ , fi := range fis {
2022-08-28 12:43:25 +03:00
if CommonSkip ( fi . Name ( ) ) {
2020-12-22 02:40:57 +03:00
continue
}
relPath := path . Join ( recPath , fi . Name ( ) )
curPath := path . Join ( dirPath , fi . Name ( ) )
if fi . IsDir ( ) {
if includeDir {
statList = append ( statList , relPath + "/" )
}
s , err := statDir ( curPath , relPath , includeDir , isDirOnly , followSymlinks )
if err != nil {
return nil , err
}
statList = append ( statList , s ... )
} else if ! isDirOnly {
statList = append ( statList , relPath )
} else if followSymlinks && fi . Mode ( ) & os . ModeSymlink != 0 {
link , err := os . Readlink ( curPath )
if err != nil {
return nil , err
}
isDir , err := IsDir ( link )
if err != nil {
return nil , err
}
if isDir {
if includeDir {
statList = append ( statList , relPath + "/" )
}
s , err := statDir ( curPath , relPath , includeDir , isDirOnly , followSymlinks )
if err != nil {
return nil , err
}
statList = append ( statList , s ... )
}
}
}
return statList , nil
}
// StatDir gathers information of given directory by depth-first.
// It returns slice of file list and includes subdirectories if enabled;
// it returns error and nil slice when error occurs in underlying functions,
// or given path is not a directory or does not exist.
//
// Slice does not include given path itself.
// If subdirectories is enabled, they will have suffix '/'.
func StatDir ( rootPath string , includeDir ... bool ) ( [ ] string , error ) {
if isDir , err := IsDir ( rootPath ) ; err != nil {
return nil , err
} else if ! isDir {
return nil , errors . New ( "not a directory or does not exist: " + rootPath )
}
isIncludeDir := false
if len ( includeDir ) != 0 {
isIncludeDir = includeDir [ 0 ]
}
return statDir ( rootPath , "" , isIncludeDir , false , false )
}
2021-04-09 01:25:57 +03:00
2022-04-01 11:47:50 +03:00
func isOSWindows ( ) bool {
return runtime . GOOS == "windows"
}
2021-07-08 14:38:13 +03:00
// FileURLToPath extracts the path information from a file://... url.
2021-04-09 01:25:57 +03:00
func FileURLToPath ( u * url . URL ) ( string , error ) {
if u . Scheme != "file" {
return "" , errors . New ( "URL scheme is not 'file': " + u . String ( ) )
}
path := u . Path
2022-04-01 11:47:50 +03:00
if ! isOSWindows ( ) {
2021-04-09 01:25:57 +03:00
return path , nil
}
// If it looks like there's a Windows drive letter at the beginning, strip off the leading slash.
re := regexp . MustCompile ( "/[A-Za-z]:/" )
if re . MatchString ( path ) {
return path [ 1 : ] , nil
}
return path , nil
}
2022-04-01 11:47:50 +03:00
// HomeDir returns path of '~'(in Linux) on Windows,
// it returns error when the variable does not exist.
func HomeDir ( ) ( home string , err error ) {
// TODO: some users run Gitea with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually)
2022-06-10 04:57:49 +03:00
// TODO: when running gitea as a sub command inside git, the HOME directory is not the user's home directory
2022-04-01 11:47:50 +03:00
// so at the moment we can not use `user.Current().HomeDir`
if isOSWindows ( ) {
home = os . Getenv ( "USERPROFILE" )
if home == "" {
home = os . Getenv ( "HOMEDRIVE" ) + os . Getenv ( "HOMEPATH" )
}
} else {
home = os . Getenv ( "HOME" )
}
if home == "" {
return "" , errors . New ( "cannot get home directory" )
}
return home , nil
}
2022-08-28 12:43:25 +03:00
// CommonSkip will check a provided name to see if it represents file or directory that should not be watched
func CommonSkip ( name string ) bool {
if name == "" {
return true
}
switch name [ 0 ] {
case '.' :
return true
case 't' , 'T' :
return name [ 1 : ] == "humbs.db"
case 'd' , 'D' :
return name [ 1 : ] == "esktop.ini"
}
return false
}