/* This file is the outcome of me starting to learn the Go programming * language. I figured I might as well follow the assignments I'm giving * my students to learn C (http://www.cs.stevens.edu/~jschauma/631/), so * here's a "trivial cp" implementation. This can probably be goified * significantly, but for what it's worth, it does what it's supposed to * and provided a good practice exercise. * - Jan Schaumann , October 2011 */ package main import ( "fmt" "os" "path" "syscall" ) const _PROGNAME string = "cp" /* perform the actual copying */ func copy(src, dest string) { var ( e, r, rfd, wfd int sb1, sb2 syscall.Stat_t ) /* check if src exists */ e = syscall.Stat(src, &sb1) if e != 0 { fmt.Fprintf(os.Stderr, "%s: %s: %s\n", _PROGNAME, src, syscall.Errstr(e)) os.Exit(1) } /* check if src is a directory */ if (syscall.S_IFDIR & sb1.Mode) != 0 { fmt.Fprintf(os.Stderr, "%s: %s is a directory\n", _PROGNAME, src) os.Exit(1) } /* if dest exists, make sure it's not the same file */ e = syscall.Stat(dest, &sb2) if e == 0 && sb1.Ino == sb2.Ino { fmt.Fprintf(os.Stderr, "%s: %s and %s are identical (not copied)\n", _PROGNAME, src, dest) os.Exit(1) } rfd, e = syscall.Open(src, syscall.O_RDONLY, 0) if rfd == -1 { fmt.Fprintf(os.Stderr, "%s: %s: %s\n", _PROGNAME, src, syscall.Errstr(e)) os.Exit(1) } defer syscall.Close(rfd) /* if dest is a directory, create the file under it */ if (syscall.S_IFDIR & sb2.Mode) != 0 { dest = dest + "/" + path.Base(src) } wfd, e = syscall.Open(dest, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_TRUNC, 0666) if wfd == -1 { fmt.Fprintf(os.Stderr, "%s: %s: %s\n", _PROGNAME, dest, syscall.Errstr(e)) os.Exit(1) } defer syscall.Close(wfd) /* shortcut for zero-length source files */ if (sb1.Size == 0) { return } var buf = make([]byte, sb1.Blksize) for { r, e = syscall.Read(rfd, buf) if r < 0 { fmt.Fprintf(os.Stderr, "%s: %s\n", _PROGNAME, syscall.Errstr(e)) os.Exit(1) } if r == 0 { break } w, e2 := syscall.Write(wfd, buf[:r]) if w != r || w == -1 { fmt.Fprintf(os.Stderr, "%s: %s\n", _PROGNAME, syscall.Errstr(e2)) /* We differ from cp(1) here in that we abort on * error; cp(1) warns and moves on to the next * file. This trivial version of cp only handles * one file, so we can exit. */ os.Exit(1) } } } /* trivially copy a file to another file or into a directory */ func main() { if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, "Usage: %s src dest\n", _PROGNAME) os.Exit(1) } argv := os.Args copy(argv[1], argv[2]) }