diff --git a/error.go b/error.go index bdd49c3..b974460 100644 --- a/error.go +++ b/error.go @@ -5,9 +5,13 @@ import ( "os" ) +func PrintToStderr(v interface{}) { + fmt.Fprintf(os.Stderr, "%v: %v\n", os.Args[0], v) +} + func ExitIfError(err error) { if err != nil { - fmt.Fprintf(os.Stderr, "%v: %v\n", os.Args[0], err) + PrintToStderr(err) os.Exit(1) } } diff --git a/md5sum/md5sum.go b/md5sum/md5sum.go new file mode 100644 index 0000000..6d5d1aa --- /dev/null +++ b/md5sum/md5sum.go @@ -0,0 +1,141 @@ +package main + +import ( + "bufio" + "crypto/md5" + "errors" + "fmt" + "hash" + "io" + "os" + "strings" + + "github.com/alexflint/go-arg" + common "source.heropunch.io/tomo/go-coreutils" +) + +var ImproperlyFormattedErr = errors.New("improperly formatted line") + +func printSumForFile(filename string, sumFunc func(io.Reader) (hash.Hash, error)) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + h, err := sumFunc(file) + if err != nil { + return err + } + + printHash(h, filename) + return nil +} + +func printSumForStdin(sumFunc func(io.Reader) (hash.Hash, error)) { + h, err := sumFunc(os.Stdin) + if err != nil { + common.PrintToStderr(err) + os.Exit(1) + } + printHash(h, "") + os.Exit(0) +} + +func md5sum(r io.Reader) (hash.Hash, error) { + h := md5.New() + if _, err := io.Copy(h, r); err != nil { + return nil, err + } + return h, nil +} + +func printHash(h hash.Hash, filename string) { + fmt.Printf("%x %v\n", h.Sum(nil), filename) +} + +func checkSumForFile(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + return checkSums(file) +} + +func checkSums(r io.Reader) error { + scanner := bufio.NewScanner(r) + + for scanner.Scan() { + err := checkMD5Sum(scanner.Text()) + if err != nil { + return err + } + } + return scanner.Err() +} + +func checkMD5Sum(line string) error { + s := strings.SplitN(line, " ", 2) + hash := s[0] + filename := s[1] + + if filename == "" { + return ImproperlyFormattedErr + } + + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + h, err := md5sum(file) + if err != nil { + return err + } + result := "OK" + if fmt.Sprintf("%x", h.Sum(nil)) != hash { + result = "FAILED" + } + fmt.Printf("%s: %s\n", filename, result) + + return nil +} + +func main() { + var args struct { + Check bool `arg:"-c"` + Files []string `arg:"positional"` + } + + arg.MustParse(&args) + + exitCode := 0 + if len(args.Files) == 0 { + if !args.Check { + printSumForStdin(md5sum) + } else { + fmt.Println("Checking from stdin") + } + } + + for _, filename := range args.Files { + if !args.Check { + err := printSumForFile(filename, md5sum) + if err != nil { + common.PrintToStderr(err) + exitCode = 1 + } + } else { + err := checkSumForFile(filename) + if err != nil { + common.PrintToStderr(err) + exitCode = 1 + } + } + } + + os.Exit(exitCode) +}