github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/cmdflag/flag.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package cmdflag handles flag processing common to several go tools.
     6  package cmdflag
     7  
     8  import (
     9  	"flag"
    10  	"fmt"
    11  	"os"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/base"
    16  )
    17  
    18  // The flag handling part of go commands such as test is large and distracting.
    19  // We can't use the standard flag package because some of the flags from
    20  // our command line are for us, and some are for the binary we're running,
    21  // and some are for both.
    22  
    23  // Defn defines a flag we know about.
    24  type Defn struct {
    25  	Name       string     // Name on command line.
    26  	BoolVar    *bool      // If it's a boolean flag, this points to it.
    27  	Value      flag.Value // The flag.Value represented.
    28  	PassToTest bool       // Pass to the test binary? Used only by go test.
    29  	Present    bool       // Flag has been seen.
    30  }
    31  
    32  // IsBool reports whether v is a bool flag.
    33  func IsBool(v flag.Value) bool {
    34  	vv, ok := v.(interface {
    35  		IsBoolFlag() bool
    36  	})
    37  	if ok {
    38  		return vv.IsBoolFlag()
    39  	}
    40  	return false
    41  }
    42  
    43  // SetBool sets the addressed boolean to the value.
    44  func SetBool(cmd string, flag *bool, value string) {
    45  	x, err := strconv.ParseBool(value)
    46  	if err != nil {
    47  		SyntaxError(cmd, "illegal bool flag value "+value)
    48  	}
    49  	*flag = x
    50  }
    51  
    52  // SetInt sets the addressed integer to the value.
    53  func SetInt(cmd string, flag *int, value string) {
    54  	x, err := strconv.Atoi(value)
    55  	if err != nil {
    56  		SyntaxError(cmd, "illegal int flag value "+value)
    57  	}
    58  	*flag = x
    59  }
    60  
    61  // SyntaxError reports an argument syntax error and exits the program.
    62  func SyntaxError(cmd, msg string) {
    63  	fmt.Fprintf(os.Stderr, "go %s: %s\n", cmd, msg)
    64  	if cmd == "test" {
    65  		fmt.Fprintf(os.Stderr, `run "go help %s" or "go help testflag" for more information`+"\n", cmd)
    66  	} else {
    67  		fmt.Fprintf(os.Stderr, `run "go help %s" for more information`+"\n", cmd)
    68  	}
    69  	base.SetExitStatus(2)
    70  	base.Exit()
    71  }
    72  
    73  // AddKnownFlags registers the flags in defns with base.AddKnownFlag.
    74  func AddKnownFlags(cmd string, defns []*Defn) {
    75  	for _, f := range defns {
    76  		base.AddKnownFlag(f.Name)
    77  		base.AddKnownFlag(cmd + "." + f.Name)
    78  	}
    79  }
    80  
    81  // Parse sees if argument i is present in the definitions and if so,
    82  // returns its definition, value, and whether it consumed an extra word.
    83  // If the flag begins (cmd.Name()+".") it is ignored for the purpose of this function.
    84  func Parse(cmd string, usage func(), defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) {
    85  	arg := args[i]
    86  	if strings.HasPrefix(arg, "--") { // reduce two minuses to one
    87  		arg = arg[1:]
    88  	}
    89  	switch arg {
    90  	case "-?", "-h", "-help":
    91  		usage()
    92  	}
    93  	if arg == "" || arg[0] != '-' {
    94  		return
    95  	}
    96  	name := arg[1:]
    97  	// If there's already a prefix such as "test.", drop it for now.
    98  	name = strings.TrimPrefix(name, cmd+".")
    99  	equals := strings.Index(name, "=")
   100  	if equals >= 0 {
   101  		value = name[equals+1:]
   102  		name = name[:equals]
   103  	}
   104  	for _, f = range defns {
   105  		if name == f.Name {
   106  			// Booleans are special because they have modes -x, -x=true, -x=false.
   107  			if f.BoolVar != nil || IsBool(f.Value) {
   108  				if equals < 0 { // Otherwise, it's been set and will be verified in SetBool.
   109  					value = "true"
   110  				} else {
   111  					// verify it parses
   112  					SetBool(cmd, new(bool), value)
   113  				}
   114  			} else { // Non-booleans must have a value.
   115  				extra = equals < 0
   116  				if extra {
   117  					if i+1 >= len(args) {
   118  						SyntaxError(cmd, "missing argument for flag "+f.Name)
   119  					}
   120  					value = args[i+1]
   121  				}
   122  			}
   123  			if f.Present {
   124  				SyntaxError(cmd, f.Name+" flag may be set only once")
   125  			}
   126  			f.Present = true
   127  			return
   128  		}
   129  	}
   130  	f = nil
   131  	return
   132  }
   133  
   134  // FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS.
   135  // Ideally the caller would mention that the flags were from GOFLAGS
   136  // when reporting errors, but that's too hard for now.
   137  func FindGOFLAGS(defns []*Defn) []string {
   138  	var flags []string
   139  	for _, flag := range base.GOFLAGS() {
   140  		// Flags returned by base.GOFLAGS are well-formed, one of:
   141  		//	-x
   142  		//	--x
   143  		//	-x=value
   144  		//	--x=value
   145  		if strings.HasPrefix(flag, "--") {
   146  			flag = flag[1:]
   147  		}
   148  		name := flag[1:]
   149  		if i := strings.Index(name, "="); i >= 0 {
   150  			name = name[:i]
   151  		}
   152  		for _, f := range defns {
   153  			if name == f.Name {
   154  				flags = append(flags, flag)
   155  				break
   156  			}
   157  		}
   158  	}
   159  	return flags
   160  }