package main import ( "errors" "fmt" "os" "github.com/spf13/cobra" "src.lwithers.me.uk/go/htpack/packed" ) var inspectCmd = &cobra.Command{ Use: "inspect", Short: "View contents of an htpack file", RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("must specify one or more files") } var exitCode int for _, filename := range args { if err := Inspect(filename); err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err) exitCode = 1 } } os.Exit(exitCode) return nil }, } // Inspect a packfile. // TODO: verify etag; verify integrity of compressed data. func Inspect(filename string) error { f, err := os.Open(filename) if err != nil { return err } defer f.Close() hdr, dir, err := packed.Load(f) if hdr != nil { fmt.Printf("Header: %#v\n", hdr) } if dir != nil { fmt.Printf("%d files:\n", len(dir.Files)) for path, info := range dir.Files { fmt.Printf(" • %s\n"+ " · Etag: %s\n"+ " · Content type: %s\n"+ " · Uncompressed: %s (offset %d)\n", path, info.Etag, info.ContentType, printSize(info.Uncompressed.Length), info.Uncompressed.Offset) if info.Gzip != nil { fmt.Printf(" · Gzipped: %s (offset %d)\n", printSize(info.Gzip.Length), info.Gzip.Offset) } if info.Brotli != nil { fmt.Printf(" · Brotli: %s (offset %d)\n", printSize(info.Brotli.Length), info.Brotli.Offset) } } inspectSummary(dir) } return err } func inspectSummary(dir *packed.Directory) { var ( n, ngzip, nbrotli int s, sgzip, sbrotli uint64 ) for _, f := range dir.Files { n++ s += f.Uncompressed.Length if f.Gzip != nil { ngzip++ sgzip += f.Gzip.Length } if f.Brotli != nil { nbrotli++ sbrotli += f.Brotli.Length } } fmt.Printf("Uncompressed:\n\tFiles: %d\n\tSize: %s\n", n, printSize(s)) if ngzip > 0 { fmt.Printf("gzip compressed:\n\tFiles: %d (%.1f%% of total)\n"+ "\tSize: %s\n\tRatio: %.1f%%\n", ngzip, 100*float64(ngzip)/float64(n), printSize(sgzip), 100*float64(sgzip)/float64(s)) } if nbrotli > 0 { fmt.Printf("brotli compressed:\n\tFiles: %d (%.1f%% of total)\n"+ "\tSize: %s\n\tRatio: %.1f%%\n", nbrotli, 100*float64(nbrotli)/float64(n), printSize(sbrotli), 100*float64(sbrotli)/float64(s)) } } func printSize(size uint64) string { switch { case size < 1<<10: return fmt.Sprintf("%d bytes", size) case size < 1<<15: return fmt.Sprintf("%.2f KiB", float64(size)/(1<<10)) case size < 1<<20: return fmt.Sprintf("%.1f KiB", float64(size)/(1<<10)) case size < 1<<25: return fmt.Sprintf("%.2f MiB", float64(size)/(1<<20)) case size < 1<<30: return fmt.Sprintf("%.1f MiB", float64(size)/(1<<20)) case size < 1<<35: return fmt.Sprintf("%.2f GiB", float64(size)/(1<<30)) default: return fmt.Sprintf("%.1f GiB", float64(size)/(1<<30)) } }