gob/shell/file/file.go

334 lines
8.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package file
import (
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
. "gitea.zaclys.com/bvaudour/gob/option"
)
var (
owners = make(map[string]string)
groups = make(map[string]string)
)
// File représente la description dun fichier sur un disque.
type File struct {
info os.FileInfo
dirname string
name string
parent Option[*File]
children FileList
}
// New récupère les informations dun fichier à partir de son nom.
// et de son répertoire dappartenance.
func New(dirname, name string) (f Result[*File]) {
info, err := os.Lstat(filepath.Join(dirname, name))
if err == nil {
return Ok(&File{
info: info,
dirname: dirname,
name: name,
})
}
return Err[*File](err)
}
// NewFromInfo agit comme New mais en ayant déjà récupéré une
// partie de la description.
func NewFromInfo(dirname string, info os.FileInfo) *File {
return &File{
info: info,
dirname: dirname,
name: info.Name(),
}
}
func (f *File) stat() *syscall.Stat_t { return f.info.Sys().(*syscall.Stat_t) }
// Mod retourne les droits UNIX du fichier.
func (f *File) Mode() os.FileMode { return f.info.Mode() }
// HasMode vérifie que le fichier a un des droits spécifiés.
func (f *File) HasMode(mask os.FileMode) bool { return f.Mode()&mask != 0 }
// IsDir retourne vrai si le fichier est un répertoire.
func (f *File) IsDir() bool { return f.info.IsDir() }
// IsSymlink retourne vrai si le fichier est un lien symbolique.
func (f *File) IsSymlink() bool { return f.HasMode(os.ModeSymlink) }
// IsDevice retourne vrai si le fichier est un fichier de périphérique.
func (f *File) IsDevice() bool { return f.HasMode(os.ModeDevice) }
// IsCharDevice retourne vrai si le fichier est un fichier de périphérique de caractère.
func (f *File) IsCharDevice() bool { return f.HasMode(os.ModeCharDevice) }
// IsSocket retourne vrai si le fichier est un socket.
func (f *File) IsSocket() bool { return f.HasMode(os.ModeSocket) }
// IsPipe retourne vrai si le fichier est de type pipe.
func (f *File) IsPipe() bool { return f.HasMode(os.ModeNamedPipe) }
// IsIrregular retourne vrai si fichier est de type inconnu.
func (f *File) IsIrregular() bool { return f.HasMode(os.ModeIrregular) }
// IsRegular retourne vrai si le fichier est un fichier régulier.
func (f *File) IsRegular() bool { return f.Mode().IsRegular() }
// IsUid retourne vrai si le fichier a la permission Suid.
func (f *File) IsUid() bool { return f.HasMode(os.ModeSetuid) }
// IsGid retourne vrai si le fichier a la permission Sgid.
func (f *File) IsGid() bool { return f.HasMode(os.ModeSetgid) }
// IsSticky retourne vrai si le fichier a la permission Sticky Bit.
func (f *File) IsSticky() bool { return f.HasMode(os.ModeSticky) }
// Name retourne le nom du fichier.
func (f *File) Name() string { return f.name }
// DirName retourne le répertoire dappartenance.
func (f *File) DirName() string { return f.dirname }
// Path retourne le chemin complet du fichier.
func (f *File) Path() string { return filepath.Join(f.dirname, f.name) }
// AbsolutePath agit comme Path mais sassure que le chemin soit absolu.
func (f *File) AbsolutePath() string {
path := f.Path()
if filepath.IsAbs(path) {
return path
}
abs, _ := filepath.Abs(path)
return abs
}
// Split retourne le dossier et le nom du fichier.
func (f *File) Split() (dirname, name string) {
path := filepath.Clean(f.Path())
return filepath.Split(path)
}
// Split retourne le dossier absolu et le nom du fichier.
func (f *File) AbsoluteSplit() (dirname, name string) {
path := f.AbsolutePath()
return filepath.Split(path)
}
// Extension retourne lextension du fichier.
func (f *File) Extension() string {
if f.IsDir() {
return ""
}
return filepath.Ext(f.info.Name())
}
// LinkPath retourne le chemin du fichier pointé
// par le lien symbolique, ou une chaîne vide si le lien est cassé
// ou que le fichier nest pas un lien symbolique.
func (f *File) LinkPath() string {
if !f.IsSymlink() {
return ""
}
lpath, _ := os.Readlink(f.Path())
return lpath
}
func ts2Date(ts syscall.Timespec) time.Time { return time.Unix(ts.Sec, ts.Nsec) }
// CreationTime retourne la date de création du fichier.
func (f *File) CreationTime() time.Time { return ts2Date(f.stat().Ctim) }
// AccessTime retourne la date daccès du fichier.
func (f *File) AccessTime() time.Time { return ts2Date(f.stat().Atim) }
// ModificationTime retourne la date de modification du fichier.
func (f *File) ModificationTime() time.Time { return f.info.ModTime() }
// Size retourne la taille (en octets) du fichier.
func (f *File) Size() int64 { return f.info.Size() }
// BlockSize retourne la taille de blocs (en octets) du fichier.
func (f *File) BlockSize() int64 { return f.stat().Blksize }
// DirSize retourne la taille totale des fichiers (en octets) contenus dans un répertoire,
// ou bien la taille du fichier sil sagit dun simple fichier.
// La taille inclut de façon récursive celle des sous-répertoires.
func (f *File) DirSize() (size int64) {
if !f.IsDir() {
return f.Size()
}
for _, c := range f.children {
size += c.DirSize()
}
return
}
// OwnerID retourne lID utilisateur du fichier.
func (f *File) OwnerID() string { return strconv.Itoa(int(f.stat().Uid)) }
// GroupID retourne lID du groupe du fichier.
func (f *File) GroupID() string { return strconv.Itoa(int(f.stat().Gid)) }
// Owner retourne le nom du propriétaire du fichier.
func (f *File) Owner() string {
id := f.OwnerID()
if owner, ok := owners[id]; ok {
return owner
}
var owner string
if e, err := user.LookupId(id); err == nil {
owner = e.Name
}
owners[id] = owner
return owner
}
// Group retourne le nom du groupe du fichier.
func (f *File) Group() string {
id := f.GroupID()
if group, ok := groups[id]; ok {
return group
}
var group string
if e, err := user.LookupGroupId(id); err == nil {
group = e.Name
}
groups[id] = group
return group
}
// Children retourne la liste des fichiers de premier niveau du répertoire
// ou rien si le fichier nest pas un répertoire.
func (f *File) Children() FileList { return f.children }
// SearchChildren recherche les fichiers dun répertoire.
// deepness indique la profondeur de recherche et doit valoir au moins 1.
// searchoptions peut contenir deux options de recherche optionnelles :
// 1. Fichiers masqués (par défaut non retournés),
// 2. Fichiers de backup (ie. ceux avec le suffixe ~) (par défaut non retournés).
func (f *File) SearchChildren(deepness int, searchOptions ...bool) (children FileList) {
if !f.IsDir() || deepness == 0 {
return
}
var hidden, backup bool
if len(searchOptions) > 0 {
hidden = searchOptions[0]
if len(searchOptions) > 1 {
backup = searchOptions[1]
}
}
dir, err := os.Open(f.Path())
if err != nil {
return
}
defer dir.Close()
infos, _ := dir.Readdir(0)
dirname := f.Path()
if deepness > 0 {
deepness--
}
var wg sync.WaitGroup
for _, i := range infos {
name := i.Name()
if (!hidden && strings.HasPrefix(name, ".")) || (!backup && strings.HasSuffix(name, "~")) {
continue
}
child := NewFromInfo(dirname, i)
children.Add(child)
wg.Add(1)
go (func(c *File) {
defer wg.Done()
c.SearchChildren(deepness, searchOptions...)
})(child)
}
wg.Wait()
f.children = children
for _, c := range children {
c.parent = Some(f)
}
return
}
// Flatten retourne la liste de tous les fichiers et répertoires de façon récursive.
// Si only_dir est présent et vaut true, seuls les répertoires sont retournés.
func (f *File) Flatten(only_dirs ...bool) (fl FileList) {
d := len(only_dirs) > 0 && only_dirs[0]
if d && !f.IsDir() {
return
}
fl.Add(f)
for _, c := range f.children {
fl.Add(c.Flatten(only_dirs...)...)
}
return
}
// Parent retourne le répertoire dappartenance.
func (f *File) Parent() Option[*File] {
return f.parent
}
// SearchParent agit comme Parent mais force la recherche
// si celui-ci nest pas encore défini.
func (f *File) SearchParent() Option[*File] {
if f.parent.IsDefined() {
return f.parent
}
d, _ := f.AbsoluteSplit()
pdirname, pname := filepath.Split(d)
p := New(pdirname, pname)
if parent, ok := p.Ok(); ok {
f.parent = Some(parent)
}
return f.parent
}
// AddChildren ajoute des fichiers en tant quappartenance à un répertoire.
func (f *File) AddChildren(children ...*File) {
for _, c := range children {
c.parent = Some(f)
}
f.children.Add(children...)
}
// SetParent définit le répertoire parent du fichier.
// Si both est donné et est vrai, le fichier est ajouté au répertoire
// parent en tant quenfant.
func (f *File) SetParent(parent Option[*File], both ...bool) {
f.parent = parent
if p, ok := parent.Get(); ok && len(both) > 0 && both[0] {
p.children.Add(f)
}
}