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  }