Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Commit

Permalink
implement builtin copy operation
Browse files Browse the repository at this point in the history
  • Loading branch information
gokcehan committed Feb 26, 2019
1 parent fd1af47 commit d6e9aec
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 13 deletions.
128 changes: 128 additions & 0 deletions copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"fmt"
"io"
"os"
"path/filepath"
)

func copySize(srcs []string) (int64, error) {
var total int64

for _, src := range srcs {
_, err := os.Stat(src)
if os.IsNotExist(err) {
return total, fmt.Errorf("src does not exist: %q", src)
}

err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("walk: %s", err)
}
total += info.Size()
return nil
})

if err != nil {
return total, err
}
}

return total, nil
}

func copyFile(src, dst string, info os.FileInfo, nums chan int64) error {
buf := make([]byte, 1024)

r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()

w, err := os.Create(dst)
if err != nil {
return err
}

for {
n, err := r.Read(buf)
if err != nil && err != io.EOF {
w.Close()
os.Remove(dst)
return err
}

if n == 0 {
break
}

if _, err := w.Write(buf[:n]); err != nil {
return err
}

nums <- int64(n)
}

if err := w.Close(); err != nil {
os.Remove(dst)
return err
}

if err := os.Chmod(dst, info.Mode()); err != nil {
os.Remove(dst)
return err
}

return nil
}

func copyAll(srcs []string, dstDir string) (nums chan int64, errs chan error) {
nums = make(chan int64, 1024)
errs = make(chan error, 1024)

go func() {
for _, src := range srcs {
dst := filepath.Join(dstDir, filepath.Base(src))

_, err := os.Stat(dst)
if !os.IsNotExist(err) {
var newPath string
for i := 1; !os.IsNotExist(err); i++ {
newPath = fmt.Sprintf("%s.~%d~", dst, i)
_, err = os.Stat(newPath)
}
dst = newPath
}

filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
errs <- fmt.Errorf("walk: %s", err)
return nil
}
rel, err := filepath.Rel(src, path)
if err != nil {
errs <- fmt.Errorf("relative: %s", err)
return nil
}
newPath := filepath.Join(dst, rel)
if info.IsDir() {
if err := os.MkdirAll(newPath, info.Mode()); err != nil {
errs <- fmt.Errorf("mkdir: %s", err)
}
nums <- info.Size()
} else {
if err := copyFile(path, newPath, info, nums); err != nil {
errs <- fmt.Errorf("copy: %s", err)
}
}
return nil
})
}

close(errs)
}()

return nums, errs
}
2 changes: 1 addition & 1 deletion eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ func (e *callExpr) eval(app *app, args []string) {
case "paste":
if cmd, ok := gOpts.cmds["paste"]; ok {
cmd.eval(app, e.args)
} else if err := app.nav.paste(); err != nil {
} else if err := app.nav.paste(app.ui); err != nil {
app.ui.printf("paste: %s", err)
return
}
Expand Down
63 changes: 56 additions & 7 deletions nav.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,22 +568,71 @@ func (nav *nav) save(cp bool) error {
return nil
}

func (nav *nav) paste() error {
list, cp, err := loadFiles()
func copyAsync(ui *ui, srcs []string, dstDir string) {
echo := &callExpr{"echo", []string{""}, 1}

_, err := os.Stat(dstDir)
if os.IsNotExist(err) {
echo.args[0] = fmt.Sprintf("error: %s", err)
ui.exprChan <- echo
return
}

total, err := copySize(srcs)
if err != nil {
echo.args[0] = fmt.Sprintf("error: %s", err)
ui.exprChan <- echo
return
}

nums, errs := copyAll(srcs, dstDir)

curr := int64(0)
lastUpdate := 0
errCount := 0
loop:
for {
select {
case n := <-nums:
curr += n
// n is usually 1024B so update roughly per 1024B x 1024 = 1MB copied
if lastUpdate++; lastUpdate >= 1024 {
lastUpdate = 0
percentage := int((100 * float64(curr)) / float64(total))
echo.args[0] = fmt.Sprintf("%d%%", percentage)
ui.exprChan <- echo
}
case err, ok := <-errs:
if !ok {
break loop
}
errCount++
echo.args[0] = fmt.Sprintf("[%d] error: %s", errCount, err)
ui.exprChan <- echo
}
}
}

func (nav *nav) paste(ui *ui) error {
srcs, cp, err := loadFiles()
if err != nil {
return err
}

if len(list) == 0 {
if len(srcs) == 0 {
return errors.New("no file in copy/cut buffer")
}

dir := nav.currDir()
dstDir := nav.currDir().path

cmd := pasteCommand(list, dir, cp)
if cp {
go copyAsync(ui, srcs, dstDir)
} else {
cmd := pasteCommand(srcs, dstDir, cp)

if err := cmd.Run(); err != nil {
return fmt.Errorf("pasting files: %s", err)
if err := cmd.Run(); err != nil {
return fmt.Errorf("pasting files: %s", err)
}
}

if err := saveFiles(nil, false); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions os.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func shellCommand(s string, args []string) *exec.Cmd {
return exec.Command(gOpts.shell, args...)
}

func pasteCommand(list []string, dir *dir, cp bool) *exec.Cmd {
func pasteCommand(list []string, dstDir string, cp bool) *exec.Cmd {
var sh string
var args []string

Expand All @@ -134,7 +134,7 @@ func pasteCommand(list []string, dir *dir, cp bool) *exec.Cmd {
args = append(args, "-i")

args = append(args, list...)
args = append(args, dir.path)
args = append(args, dstDir)

return exec.Command(sh, args...)
}
Expand Down
6 changes: 3 additions & 3 deletions os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func shellCommand(s string, args []string) *exec.Cmd {
return exec.Command(gOpts.shell, args...)
}

func pasteCommand(list []string, dir *dir, cp bool) *exec.Cmd {
func pasteCommand(list []string, dstDir string, cp bool) *exec.Cmd {
var args []string

sh := "robocopy"
Expand All @@ -105,9 +105,9 @@ func pasteCommand(list []string, dir *dir, cp bool) *exec.Cmd {
base := filepath.Base(f)
dest := filepath.Dir(f)
if stat.IsDir() {
exec.Command(sh, append(args, f, filepath.Join(dir.path, base))...).Run()
exec.Command(sh, append(args, f, filepath.Join(dstDir, base))...).Run()
} else {
exec.Command(sh, append(args, dest, dir.path, base)...).Run()
exec.Command(sh, append(args, dest, dstDir, base)...).Run()
}
}

Expand Down

0 comments on commit d6e9aec

Please sign in to comment.