Source

go-wise / cptree.go

/* Copy directory tree */
// FIXME: Update to go1 filepath.Walk

package main

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

type treeCopier struct {
	src, dest string
	verbose   bool

	// Last error, Visit* function will return immediatly when it's not nil
	err error
}

// Destination path.
func (c *treeCopier) destPath(src string) string {
	return filepath.Join(c.dest, src[len(c.src):])
}

// Logging (when in verbose)
func (c *treeCopier) log(format string, args ...interface{}) {
	if !c.verbose {
		return
	}
	fmt.Printf(format, args...)
	fmt.Println()
}

// The two function below are needed to satisfy filepath.Visitor interface. 
// TODO: filepath.Walk has changed as of weekly-2011-09-16

// Visit a directory, this will create the destination directory.
func (c *treeCopier) VisitDir(path string, f os.FileInfo) bool {
	if c.err != nil {
		return false
	}
	c.log("creating %s", c.destPath(path))
	c.err = os.Mkdir(c.destPath(path), f.Mode())
	return c.err == nil
}

// Visit a file, copy to destination. Parent directories should have been
// created already
func (c *treeCopier) VisitFile(path string, f os.FileInfo) {
	if c.err != nil {
		return
	}
	var src, dest *os.File
	src, c.err = os.Open(path)
	if c.err != nil {
		return
	}
	defer src.Close()

	c.log("%s -> %s", path, c.destPath(path))

	flags := os.O_WRONLY | os.O_CREATE
	dest, c.err = os.OpenFile(c.destPath(path), flags, f.Mode())
	if c.err != nil {
		return
	}
	defer dest.Close()

	var size int64
	size, c.err = io.Copy(dest, src)
	if c.err != nil {
		return
	}

	if size != f.Size() {
		c.err = errors.New(fmt.Sprintf("%s: %d/%d copied", path, size, f.Size()))
		return
	}
}

// Copy a directory tree from `src` to `dest`
func CopyTree(src, dest string, verbose bool) error {
	c := &treeCopier{src: src, dest: dest, verbose: verbose}
	filepath.Walk(src, c, nil)
	return c.err
}

func main() {
	flag.Usage = func() {
		fmt.Println("usage: cptree SRC DEST\noptions:")
		flag.PrintDefaults()
	}

	verbose := flag.Bool("verbose", false, "Be verbose")

	flag.Parse()
	if flag.NArg() != 2 {
		fmt.Println("error: missing argument")
		flag.Usage()
	}

	err := CopyTree(flag.Arg(0), flag.Arg(1), *verbose)
	if err != nil {
		fmt.Printf("error: %s\n", err)
		os.Exit(1)
	}
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.