You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
2.8 KiB
100 lines
2.8 KiB
|
1 week ago
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"io/fs"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"g.activething.com/go/DateKeys/capsule"
|
||
|
|
)
|
||
|
|
|
||
|
|
// collect returns the files named by the -in arguments: a file by its name,
|
||
|
|
// and a folder by its name followed by the path of each file below it, as a
|
||
|
|
// browser names the files of a folder (webkitRelativePath). Folders are
|
||
|
|
// walked with Lstat, following no link, and only regular files are
|
||
|
|
// accepted. The files that systems create on their own are left out of
|
||
|
|
// folders, and listed in skipped (spec §62.1 rule 15). With mtime, each
|
||
|
|
// file keeps its modification time (rule 16).
|
||
|
|
func collect(ins []string, mtime bool) (sources []capsule.Source, skipped []string, err error) {
|
||
|
|
for _, in := range ins {
|
||
|
|
abs, err := filepath.Abs(in)
|
||
|
|
if err != nil {
|
||
|
|
return nil, nil, err
|
||
|
|
}
|
||
|
|
base := filepath.Base(abs)
|
||
|
|
info, err := os.Lstat(abs)
|
||
|
|
if err != nil {
|
||
|
|
return nil, nil, err
|
||
|
|
}
|
||
|
|
switch {
|
||
|
|
case info.Mode().IsRegular():
|
||
|
|
sources = append(sources, fileSource(abs, base, info, mtime))
|
||
|
|
continue
|
||
|
|
case !info.IsDir():
|
||
|
|
return nil, nil, notRegular(in, info)
|
||
|
|
}
|
||
|
|
err = filepath.WalkDir(abs, func(p string, d fs.DirEntry, err error) error {
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if p == abs {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if systemFile(d.Name(), d.IsDir()) {
|
||
|
|
skipped = append(skipped, p)
|
||
|
|
if d.IsDir() {
|
||
|
|
return filepath.SkipDir
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if d.IsDir() {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
info, err := d.Info()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if !info.Mode().IsRegular() {
|
||
|
|
return notRegular(p, info)
|
||
|
|
}
|
||
|
|
rel, err := filepath.Rel(abs, p)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
sources = append(sources, fileSource(p, base+"/"+filepath.ToSlash(rel), info, mtime))
|
||
|
|
return nil
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return nil, nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return sources, skipped, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func notRegular(path string, info fs.FileInfo) error {
|
||
|
|
return fmt.Errorf("%s is not a regular file (%s): only regular files are encrypted, and links are not followed", path, info.Mode().Type())
|
||
|
|
}
|
||
|
|
|
||
|
|
// fileSource is the Source of the local file at path, stored as name.
|
||
|
|
func fileSource(path, name string, info fs.FileInfo, mtime bool) capsule.Source {
|
||
|
|
s := capsule.Source{Path: name, Size: info.Size(), Open: func() (io.ReadCloser, error) { return os.Open(path) }}
|
||
|
|
if mtime {
|
||
|
|
s.ModTime = info.ModTime()
|
||
|
|
}
|
||
|
|
return s
|
||
|
|
}
|
||
|
|
|
||
|
|
// systemFile reports the files and folders that systems create on their
|
||
|
|
// own, which the official SDK leaves out by default: .DS_Store, Thumbs.db,
|
||
|
|
// desktop.ini, ._* and __MACOSX (spec §62.1 rule 15).
|
||
|
|
func systemFile(name string, dir bool) bool {
|
||
|
|
if dir {
|
||
|
|
return name == "__MACOSX"
|
||
|
|
}
|
||
|
|
return strings.EqualFold(name, ".DS_Store") || strings.EqualFold(name, "Thumbs.db") ||
|
||
|
|
strings.EqualFold(name, "desktop.ini") || strings.HasPrefix(name, "._")
|
||
|
|
}
|