diff --git a/exifcopy.go b/exifcopy.go index 8285782..f28b7c6 100644 --- a/exifcopy.go +++ b/exifcopy.go @@ -115,21 +115,21 @@ func copyMetadata(outImagePath, imagePath, metadataImagePath string) error { if err != nil { return err } - defer outFile.Close() + defer func() { _ = outFile.Close() }() writer := bufio.NewWriter(outFile) imageFile, err := os.Open(imagePath) if err != nil { return err } - defer imageFile.Close() + defer func() { _ = imageFile.Close() }() imageReader := bufio.NewReader(imageFile) metaFile, err := os.Open(metadataImagePath) if err != nil { return err } - defer metaFile.Close() + defer func() { _ = metaFile.Close() }() metaReader := bufio.NewReader(metaFile) _, err = writer.Write([]byte{0xFF, soi}) @@ -166,6 +166,6 @@ func exifCopy(fromPath, toPath string) error { if err != nil { return err } - defer os.Remove(copyPath) + defer func() { _ = os.Remove(copyPath) }() return copyMetadata(toPath, copyPath, fromPath) } diff --git a/goptimize.go b/goptimize.go index f1669b0..9b51230 100644 --- a/goptimize.go +++ b/goptimize.go @@ -18,7 +18,7 @@ import ( ) // Goptimize downscales and optimizes an existing image -func Goptimize(file string) { +func goptimize(file string) { info, err := os.Stat(file) if err != nil { @@ -57,7 +57,7 @@ func Goptimize(file string) { if format.String() == "GIF" { // return if GIF is animated - unsupported - if err := IsGIFAnimated(file); err != nil { + if err := isGIFAnimated(file); err != nil { fmt.Printf("Error: animated GIF not supported (%v)\n", file) return } @@ -99,7 +99,7 @@ func Goptimize(file string) { return } - defer os.Remove(tmpFile.Name()) + defer func() { _ = os.Remove(tmpFile.Name()) }() switch imgType := format.String(); imgType { case "JPEG": @@ -127,15 +127,18 @@ func Goptimize(file string) { // immediately close the temp file to release pointers // so we can modify it with system processes - tmpFile.Close() + if err := tmpFile.Close(); err != nil { + fmt.Printf("Error closing temporary file: %v\n", err) + return + } // run through optimizers if format.String() == "JPEG" { // run one or the other, running both has no advantage if jpegtran != "" { - RunOptimizer(tmpFilename, true, jpegtran, "-optimize", "-outfile") + runOptimizer(tmpFilename, true, jpegtran, "-optimize", "-outfile") } else if jpegoptim != "" { - RunOptimizer(tmpFilename, false, jpegoptim, "-f", "-s", "-o") + runOptimizer(tmpFilename, false, jpegoptim, "-f", "-s", "-o") } if copyExif { @@ -146,13 +149,13 @@ func Goptimize(file string) { } } else if format.String() == "PNG" { if pngquant != "" { - RunOptimizer(tmpFilename, true, pngquant, "-f", "--output") + runOptimizer(tmpFilename, true, pngquant, "-f", "--output") } if optipng != "" { - RunOptimizer(tmpFilename, true, optipng, "-out") + runOptimizer(tmpFilename, true, optipng, "-out") } } else if format.String() == "GIF" && gifsicle != "" { - RunOptimizer(tmpFilename, true, gifsicle, "-o") + runOptimizer(tmpFilename, true, gifsicle, "-o") } // re-open modified temporary file @@ -162,7 +165,7 @@ func Goptimize(file string) { return } - defer tmpFile.Close() + defer func() { _ = tmpFile.Close() }() // get th eoriginal file stats srcStat, _ := os.Stat(file) @@ -187,7 +190,7 @@ func Goptimize(file string) { return } - defer out.Close() + defer func() { _ = out.Close() }() if _, err := io.Copy(out, tmpFile); err != nil { fmt.Printf("Error overwriting original file: %v\n", err) @@ -201,22 +204,26 @@ func Goptimize(file string) { } } - fmt.Printf("Goptimized %s (%dx%d %s > %s %v%%)\n", dstFile, resultW, resultH, ByteCountSI(srcSize), ByteCountSI(dstSize), savedPercent) + fmt.Printf("Optimized %s (%dx%d %s > %s %v%%)\n", dstFile, resultW, resultH, byteCountSI(srcSize), byteCountSI(dstSize), savedPercent) } else { // if the output directory is not the same, // then write a copy of the original file if outputDir != "" { out, err := os.Create(dstFile) + if err != nil { + fmt.Printf("Error creating new file: %v\n", err) + return + } + + defer func() { _ = out.Close() }() + + orig, err := os.Open(file) if err != nil { fmt.Printf("Error opening original file: %v\n", err) return } - defer out.Close() - - orig, _ := os.Open(file) - - defer orig.Close() + defer func() { _ = orig.Close() }() if _, err := io.Copy(out, orig); err != nil { fmt.Printf("Error ovewriting original file: %v\n", err) @@ -230,36 +237,35 @@ func Goptimize(file string) { } } - fmt.Printf("Copied %s (%dx%d %s %v%%)\n", dstFile, srcW, srcH, ByteCountSI(srcSize), 0) + fmt.Printf("Copied %s (%dx%d %s %v%%)\n", dstFile, srcW, srcH, byteCountSI(srcSize), 0) } else { // we didn't actually change anything - fmt.Printf("Skipped %s (%dx%d %s %v%%)\n", dstFile, srcW, srcH, ByteCountSI(srcSize), 0) + fmt.Printf("Skipped %s (%dx%d %s %v%%)\n", dstFile, srcW, srcH, byteCountSI(srcSize), 0) } } } -// RunOptimizer will run the specified command on a copy of the temporary file, +// runOptimizer will run the specified command on a copy of the temporary file, // and overwrite it if the output is smaller than the original -func RunOptimizer(src string, outFileArg bool, args ...string) { +func runOptimizer(src string, outFileArg bool, args ...string) { // create a new temp file - tmpFile, err := os.CreateTemp(os.TempDir(), "Goptimized-") + tmpFile, err := os.CreateTemp(os.TempDir(), "goptimize-") if err != nil { fmt.Printf("Cannot create temporary file: %v\n", err) return } - defer os.Remove(tmpFile.Name()) + defer func() { _ = os.Remove(tmpFile.Name()) }() source, err := os.Open(src) - if err != nil { fmt.Printf("Cannot open temporary file: %v\n", err) return } - defer source.Close() + defer func() { _ = source.Close() }() if _, err := io.Copy(tmpFile, source); err != nil { fmt.Printf("Cannot copy source file: %v\n", err) @@ -289,8 +295,8 @@ func RunOptimizer(src string, outFileArg bool, args ...string) { dstSize := dstStat.Size() // ensure file pointers are closed before renaming - tmpFile.Close() - source.Close() + func() { _ = tmpFile.Close() }() + func() { _ = source.Close() }() if dstSize < srcSize { if err := os.Rename(tmpFilename, src); err != nil { @@ -301,7 +307,7 @@ func RunOptimizer(src string, outFileArg bool, args ...string) { } // ByteCountSI returns a human readable size from int64 bytes -func ByteCountSI(b int64) string { +func byteCountSI(b int64) string { const unit = 1000 if b < unit { return fmt.Sprintf("%dB", b) @@ -315,9 +321,12 @@ func ByteCountSI(b int64) string { } // IsGIFAnimated will return an error if the GIF file has more than 1 frame -func IsGIFAnimated(gifFile string) error { - file, _ := os.Open(gifFile) - defer file.Close() +func isGIFAnimated(gifFile string) error { + file, err := os.Open(gifFile) + if err != nil { + return fmt.Errorf("cannot open GIF file: %v", err) + } + defer func() { _ = file.Close() }() g, err := gif.DecodeAll(file) if err != nil { @@ -329,5 +338,5 @@ func IsGIFAnimated(gifFile string) error { return nil } - return fmt.Errorf("Animated gif") + return fmt.Errorf("cannot optimize an animated gif") } diff --git a/main.go b/main.go index d9f9528..3baf765 100644 --- a/main.go +++ b/main.go @@ -55,11 +55,11 @@ func main() { fmt.Println("\nDetected optimizers:") if err := displayDetectedOptimizer("jpegtran ", jpegtran); err != nil { - displayDetectedOptimizer("jpegoptim", jpegoptim) + _ = displayDetectedOptimizer("jpegoptim", jpegoptim) } - displayDetectedOptimizer("optipng ", optipng) - displayDetectedOptimizer("pngquant ", pngquant) - displayDetectedOptimizer("gifsicle ", gifsicle) + _ = displayDetectedOptimizer("optipng ", optipng) + _ = displayDetectedOptimizer("pngquant ", pngquant) + _ = displayDetectedOptimizer("gifsicle ", gifsicle) } var maxSizes string @@ -85,7 +85,9 @@ func main() { flag.SortFlags = false // parse args excluding os.Args[0] - flag.Parse(os.Args[1:]) + if err := flag.Parse(os.Args[1:]); err != nil { + fmt.Printf("Error parsing flags: %s\n", err.Error()) + } // detect optimizer paths gifsicle, _ = exec.LookPath(gifsicle) @@ -179,7 +181,7 @@ func main() { for i := 0; i < threads; i++ { go func() { for nextFile := range processChan { - Goptimize(nextFile) + goptimize(nextFile) } // Channel was closed, so we finished this goroutine. wg.Done() // Let main goroutine know we are done.