blob: bb8c53eea176f56f667181eb24ce3b5152ef4f5a [file] [log] [blame]
// Package utils contains various utility functions to support the
// main tools-golang packages.
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
package utils
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// GetAllFilePaths takes a path to a directory (including an optional slice of
// path patterns to ignore), and returns a slice of relative paths to all files
// in that directory and its subdirectories (excluding those that are ignored).
func GetAllFilePaths(dirRoot string, pathsIgnored []string) ([]string, error) {
// paths is a _pointer_ to a slice -- not just a slice.
// this is so that it can be appropriately modified by append
// in the sub-function.
paths := &[]string{}
prefix := strings.TrimSuffix(dirRoot, "/")
err := filepath.Walk(dirRoot, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// don't include path if it's a directory
if fi.IsDir() {
return nil
}
// don't include path if it's a symbolic link
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
return nil
}
shortPath := strings.TrimPrefix(path, prefix)
// don't include path if it should be ignored
if pathsIgnored != nil && ShouldIgnore(shortPath, pathsIgnored) {
return nil
}
// if we got here, record the path
*paths = append(*paths, shortPath)
return nil
})
return *paths, err
}
// GetHashesForFilePath takes a path to a file on disk, and returns
// SHA1, SHA256 and MD5 hashes for that file as strings.
func GetHashesForFilePath(p string) (string, string, string, error) {
f, err := os.Open(p)
if err != nil {
return "", "", "", err
}
defer f.Close()
var ssha1, ssha256, smd5 string
hSHA1 := sha1.New()
hSHA256 := sha256.New()
hMD5 := md5.New()
hMulti := io.MultiWriter(hSHA1, hSHA256, hMD5)
if _, err := io.Copy(hMulti, f); err != nil {
f.Close()
return "", "", "", err
}
ssha1 = fmt.Sprintf("%x", hSHA1.Sum(nil))
ssha256 = fmt.Sprintf("%x", hSHA256.Sum(nil))
smd5 = fmt.Sprintf("%x", hMD5.Sum(nil))
return ssha1, ssha256, smd5, nil
}
// ShouldIgnore compares a file path to a slice of file path patterns,
// and determines whether that file should be ignored because it matches
// any of those patterns.
func ShouldIgnore(fileName string, pathsIgnored []string) bool {
fDirs, fFile := filepath.Split(fileName)
for _, pattern := range pathsIgnored {
// split into dir(s) and filename
patternDirs, patternFile := filepath.Split(pattern)
patternDirStars := strings.HasPrefix(patternDirs, "**")
if patternDirStars {
patternDirs = patternDirs[2:]
}
// case 1: specific file
if !patternDirStars && patternDirs == fDirs && patternFile != "" && patternFile == fFile {
return true
}
// case 2: all files in specific directory
if !patternDirStars && strings.HasPrefix(fDirs, patternDirs) && patternFile == "" {
return true
}
// case 3: specific file in any dir
if patternDirStars && patternDirs == "/" && patternFile != "" && patternFile == fFile {
return true
}
// case 4: specific file in any matching subdir
if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile != "" && patternFile == fFile {
return true
}
// case 5: any file in any matching subdir
if patternDirStars && strings.Contains(fDirs, patternDirs) && patternFile == "" {
return true
}
}
// if no match, don't ignore
return false
}