feat: drag files between file panes (move or copy, SSH hosts too) and into a terminal of the same machine
This commit is contained in:
1 parent
a30b89f1d8
commit
f2740c50b4
12 files changed
+484
-32
No files matched your search
@@ -7,6 +7,7 @@ package agent
|
||||
// for sshfsIdle is closed.
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
@@ -523,3 +524,104 @@ func (a *Agent) doSSHFSPut(ctx context.Context, env proto.TaskEnvelope) proto.Ta
|
||||
data, _ := json.Marshal(map[string]any{"path": args.Path, "size": args.Size})
|
||||
return proto.TaskResult{OK: true, Data: data}
|
||||
}
|
||||
|
||||
// renameTar copies a tar stream, giving its top entry base - and everything
|
||||
// under it - the name name: what the host's tar packs under the file's own
|
||||
// name arrives under the name chosen for it. Entries other than base (the
|
||||
// "._" companions a macOS tar adds) are left out.
|
||||
func renameTar(w io.Writer, r io.Reader, base, name string) error {
|
||||
tr, tw := tar.NewReader(r), tar.NewWriter(w)
|
||||
for {
|
||||
h, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
return tw.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := strings.TrimPrefix(h.Name, "./")
|
||||
top, rest := n, ""
|
||||
if i := strings.IndexByte(n, '/'); i >= 0 {
|
||||
top, rest = n[:i], n[i:]
|
||||
}
|
||||
if top != base {
|
||||
continue
|
||||
}
|
||||
h.Name = name + rest
|
||||
h.Uid, h.Gid, h.Uname, h.Gname = 0, 0, "", ""
|
||||
if err := tw.WriteHeader(h); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(tw, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// packRemote writes src on the connection's host as a tar stream named name,
|
||||
// counting what goes for the progress.
|
||||
func (a *Agent) packRemote(ctx context.Context, w io.Writer, c *sshfsConn, src, name string, m *shipMeter) error {
|
||||
s, err := a.sshfsSession(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
go func() { <-ctx.Done(); s.Close() }()
|
||||
out, _ := s.StdoutPipe()
|
||||
var errb bytes.Buffer
|
||||
s.Stderr = &limitBuf{b: &errb, max: 4096}
|
||||
src = path.Clean(src)
|
||||
if err := s.Start("COPYFILE_DISABLE=1 tar -cf - -C " + shq(path.Dir(src)) + " -- " + shq(path.Base(src))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := renameTar(w, &countReader{r: out, n: &m.done}, path.Base(src), name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Wait(); err != nil {
|
||||
if msg := strings.TrimSpace(errb.String()); msg != "" {
|
||||
return errors.New("tar на хосте: " + msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// remoteSize is about how many bytes src takes on the host, for progress.
|
||||
func (a *Agent) remoteSize(c *sshfsConn, src string) int64 {
|
||||
out, err := a.sshfsRun(c, "du -sk -- "+shq(src), nil)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
f := strings.Fields(string(out))
|
||||
if len(f) == 0 {
|
||||
return 0
|
||||
}
|
||||
kb, _ := strconv.ParseInt(f[0], 10, 64)
|
||||
return kb * 1024
|
||||
}
|
||||
|
||||
// unpackRemote puts a tar stream into dir on the connection's host, by the
|
||||
// conflict rule: tar keeps or replaces what is there, an "error" rule
|
||||
// refuses when name is there already.
|
||||
func (a *Agent) unpackRemote(c *sshfsConn, r io.Reader, dir, name, conflict string) error {
|
||||
target := strings.TrimSuffix(dir, "/") + "/" + name
|
||||
out, _ := a.sshfsRun(c, "test -e "+shq(target)+" -o -L "+shq(target)+" && echo LPRA_EXISTS; tar --version 2>/dev/null | head -n 1", nil)
|
||||
if strings.Contains(string(out), "LPRA_EXISTS") && conflict != "replace" && conflict != "skip" {
|
||||
return errors.New(target + " уже существует")
|
||||
}
|
||||
gnu := strings.Contains(string(out), "GNU tar")
|
||||
flags := ""
|
||||
switch conflict {
|
||||
case "skip":
|
||||
flags = " -k"
|
||||
if gnu {
|
||||
flags = " --skip-old-files"
|
||||
}
|
||||
case "replace":
|
||||
if gnu {
|
||||
flags = " --overwrite"
|
||||
}
|
||||
}
|
||||
_, err := a.sshfsRun(c, "mkdir -p -- "+shq(dir)+" && cd -- "+shq(dir)+" && tar -xf -"+flags, r)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user