github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/pkg/gzip/options.go (about) 1 package gzip 2 3 import ( 4 "errors" 5 "flag" 6 "fmt" 7 "os" 8 "path/filepath" 9 "runtime" 10 11 "github.com/klauspost/pgzip" 12 ) 13 14 // Options represents the CLI options possible, controlling how 15 // gzip operates on the given input data. 16 type Options struct { 17 Blocksize int 18 Level int 19 Processes int 20 Decompress bool 21 Force bool 22 Help bool 23 Keep bool 24 Quiet bool 25 Stdin bool 26 Stdout bool 27 Test bool 28 Verbose bool 29 Suffix string 30 } 31 32 // ParseArgs takes CLI args and parses them via a Flagset into fields in 33 // the Options struct. Returns any errors from parsing and validating options. 34 func (o *Options) ParseArgs(args []string, cmdLine *flag.FlagSet) error { 35 var levels [10]bool 36 37 cmdLine.IntVar(&o.Blocksize, "b", 128, "Set compression block size in KiB") 38 cmdLine.BoolVar(&o.Decompress, "d", false, "Decompress the compressed input") 39 cmdLine.BoolVar(&o.Force, "f", false, "Force overwrite of output file and compress links") 40 cmdLine.BoolVar(&o.Help, "h", false, "Display a help screen and quit") 41 cmdLine.BoolVar(&o.Keep, "k", false, "Do not delete original file after processing") 42 // TODO: implement list option here 43 cmdLine.IntVar(&o.Processes, "p", runtime.NumCPU(), "Allow up to n compression threads") 44 cmdLine.BoolVar(&o.Quiet, "q", false, "Print no messages, even on error") 45 // TODO: implement recursive option here 46 cmdLine.BoolVar(&o.Stdout, "c", false, "Write all processed output to stdout (won't delete)") 47 cmdLine.StringVar(&o.Suffix, "S", ".gz", "Specify suffix for compression") 48 cmdLine.BoolVar(&o.Test, "t", false, "Test the integrity of the compressed input") 49 cmdLine.BoolVar(&o.Verbose, "v", false, "Produce more verbose output") 50 cmdLine.BoolVar(&levels[1], "1", false, "Compression Level 1") 51 cmdLine.BoolVar(&levels[2], "2", false, "Compression Level 2") 52 cmdLine.BoolVar(&levels[3], "3", false, "Compression Level 3") 53 cmdLine.BoolVar(&levels[4], "4", false, "Compression Level 4") 54 cmdLine.BoolVar(&levels[5], "5", false, "Compression Level 5") 55 cmdLine.BoolVar(&levels[6], "6", false, "Compression Level 6") 56 cmdLine.BoolVar(&levels[7], "7", false, "Compression Level 7") 57 cmdLine.BoolVar(&levels[8], "8", false, "Compression Level 8") 58 cmdLine.BoolVar(&levels[9], "9", false, "Compression Level 9") 59 60 if err := cmdLine.Parse(args[1:]); err != nil { 61 return err 62 } 63 64 var err error 65 o.Level, err = parseLevels(levels) 66 if err != nil { 67 return fmt.Errorf("%s\n\n", err) 68 } 69 70 return o.validate(len(cmdLine.Args()) > 0) 71 } 72 73 // Validate checks options. 74 // Forces decompression to be enabled when test mode is enabled. 75 // It further modifies options if the running binary is named 76 // gunzip or gzcat to allow for expected behavor. Checks if there is piped stdin data. 77 func (o *Options) validate(moreArgs bool) error { 78 if o.Help { 79 // Return an empty errorString so the CLI app does not continue 80 return errors.New("") 81 } 82 83 if o.Test { 84 o.Decompress = true 85 } 86 87 // Support gunzip and gzcat symlinks 88 if filepath.Base(os.Args[0]) == "gunzip" { 89 o.Decompress = true 90 } else if filepath.Base(os.Args[0]) == "gzcat" { 91 o.Decompress = true 92 o.Stdout = true 93 } 94 95 // Stat os.Stdin and ignore errors. stat will be a nil FileInfo if there is an 96 // error. 97 stat, _ := os.Stdin.Stat() 98 99 // No files passed and arguments and Stdin piped data found. 100 // Stdin piped data is ignored if arguments are found. 101 if !moreArgs && (stat.Mode()&os.ModeNamedPipe) != 0 { 102 o.Stdin = true 103 // Enable force to ignore suffix checks 104 o.Force = true 105 // Since there's no filename to derive the output path from, only support 106 // outputting to stdout when data is piped from stdin 107 o.Stdout = true 108 } else if !moreArgs { 109 // No stdin piped data found and no files passed as arguments 110 return fmt.Errorf("error: no input files specified or piped data") 111 } 112 113 return nil 114 } 115 116 // parseLevels loops through a [10]bool and returns the index of the element 117 // that's true. If more than one element is true it returns an error. If no 118 // element is true, it returns the constant pgzip.DefaultCompression (-1). 119 func parseLevels(levels [10]bool) (int, error) { 120 var level int 121 122 for i, l := range levels { 123 if l && level != 0 { 124 return 0, fmt.Errorf("error: multiple compression levels specified") 125 } else if l { 126 level = i 127 } 128 } 129 130 if level == 0 { 131 return pgzip.DefaultCompression, nil 132 } 133 134 return level, nil 135 }