2022-06-28 13:11:16 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2022-08-03 21:28:45 +02:00
|
|
|
"sort"
|
2022-06-28 13:11:16 +02:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FileEntry struct {
|
2022-08-16 22:12:26 +02:00
|
|
|
path string
|
|
|
|
|
2022-06-28 13:11:16 +02:00
|
|
|
size *uint64
|
|
|
|
modSize *uint64
|
2022-08-16 22:12:26 +02:00
|
|
|
|
|
|
|
fileCount *uint64
|
|
|
|
modFileCount *uint64
|
|
|
|
|
|
|
|
parent Entry
|
2022-06-28 13:11:16 +02:00
|
|
|
|
|
|
|
removal bool
|
|
|
|
noPerm bool
|
|
|
|
isMount bool
|
|
|
|
isExec bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Entry = &FileEntry{}
|
|
|
|
|
|
|
|
func (e *FileEntry) Name() string {
|
|
|
|
return filepath.Base(e.path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Path() string {
|
|
|
|
return e.path
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Size() uint64 {
|
|
|
|
if e.size == nil {
|
|
|
|
e.Scan(nil, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
return *e.size
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Entries() []Entry {
|
|
|
|
return []Entry{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) String() string {
|
|
|
|
var ic string
|
|
|
|
if e.isExec {
|
|
|
|
ic = icon("app")
|
|
|
|
} else {
|
|
|
|
ic = icon(filepath.Ext(e.Path()))
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.removal {
|
|
|
|
return ColorRed.Sprintf(
|
|
|
|
"%s %s (%s / %s)",
|
|
|
|
ic, e.Name(), fmtSize(e.SizeModified()), fmtSize(e.Size()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.isExec {
|
|
|
|
return ColorGreen.Sprintf("%s %s (%s)", ic, e.Name(), fmtSize(e.Size()))
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s %s (%s)", ic, e.Name(), fmtSize(e.Size()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) stringRecursive(depth, maxDepth int, b *strings.Builder) {
|
|
|
|
if depth > maxDepth {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b.WriteString(strings.Repeat(" ", depth))
|
|
|
|
b.WriteString(e.String())
|
|
|
|
b.WriteRune('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) StringRecursive(depth int) string {
|
|
|
|
b := new(strings.Builder)
|
|
|
|
e.stringRecursive(0, depth, b)
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Scan(ch chan<- string, mounts map[string]struct{}) {
|
|
|
|
if ch != nil {
|
|
|
|
ch <- e.Path()
|
|
|
|
}
|
|
|
|
|
|
|
|
s := uint64(0)
|
|
|
|
if fi, err := os.Lstat(e.Path()); err == nil {
|
|
|
|
s = uint64(fi.Size())
|
|
|
|
e.isExec = fi.Mode()&0x41 > 0 // 0x41 == 0b001000001 == rwXrwxrwX
|
|
|
|
} else if errors.Is(err, os.ErrPermission) {
|
|
|
|
e.noPerm = true
|
|
|
|
}
|
|
|
|
|
2022-08-16 22:12:26 +02:00
|
|
|
e.size = &s
|
2022-06-28 13:11:16 +02:00
|
|
|
e.modSize = new(uint64)
|
|
|
|
*e.modSize = *e.size
|
2022-08-16 22:12:26 +02:00
|
|
|
|
|
|
|
f := uint64(1)
|
|
|
|
e.fileCount = &f
|
|
|
|
e.modFileCount = new(uint64)
|
|
|
|
*e.modFileCount = *e.fileCount
|
2022-06-28 13:11:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Parent() Entry {
|
|
|
|
return e.parent
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) Entry(path string) Entry {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) IsDir() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) SetRemovalMark(mark bool) {
|
|
|
|
e.removal = mark
|
|
|
|
if mark {
|
2022-08-16 22:12:26 +02:00
|
|
|
e.modify(-*e.size, -*e.fileCount) // underflow that is cancelling out perfectly
|
2022-06-28 13:11:16 +02:00
|
|
|
} else {
|
2022-08-16 22:12:26 +02:00
|
|
|
e.modify(*e.size, *e.fileCount)
|
2022-06-28 13:11:16 +02:00
|
|
|
}
|
2022-08-03 21:28:45 +02:00
|
|
|
|
|
|
|
// recalculate order of parent entries
|
|
|
|
pentries := e.Parent().Entries()
|
|
|
|
sort.Slice(pentries, func(i, j int) bool {
|
|
|
|
return pentries[i].SizeModified() < pentries[j].SizeModified()
|
|
|
|
})
|
2022-06-28 13:11:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) RemovalMark() bool {
|
|
|
|
return e.removal
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) SizeModified() uint64 {
|
|
|
|
if e.size == nil {
|
|
|
|
e.Size()
|
|
|
|
}
|
|
|
|
|
|
|
|
return *e.modSize
|
|
|
|
}
|
|
|
|
|
2022-08-16 22:12:26 +02:00
|
|
|
func (e *FileEntry) modify(size uint64, fileCount uint64) {
|
|
|
|
*e.modSize = *e.modSize + size
|
|
|
|
*e.modFileCount = *e.modFileCount + fileCount
|
2022-06-28 13:11:16 +02:00
|
|
|
if e.parent != nil {
|
2022-08-16 22:12:26 +02:00
|
|
|
e.parent.modify(size, fileCount)
|
2022-06-28 13:11:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) RemovalStats(stats *RemovalStats) {
|
|
|
|
if e.RemovalMark() {
|
|
|
|
stats.Files++
|
|
|
|
stats.Bytes += *e.size
|
|
|
|
if e.Parent() != nil && !e.Parent().RemovalMark() {
|
|
|
|
stats.Paths = append(stats.Paths, e.Path())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-16 22:12:26 +02:00
|
|
|
|
|
|
|
func (e *FileEntry) FileCount() uint64 {
|
|
|
|
return *e.fileCount
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *FileEntry) FileCountModified() uint64 {
|
|
|
|
return *e.modFileCount
|
|
|
|
}
|